From 9de5608f76b4e60d97aeb0fc9409685db69c27e0 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 13:05:50 +0000 Subject: [PATCH 01/69] feat(nodes): add HW video codec backends (Vulkan Video H.264, VA-API AV1, NVENC/NVDEC AV1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement hardware-accelerated video encoding and decoding for StreamKit, targeting Linux with Intel and NVIDIA GPUs (issue #217). Three backends behind optional feature flags: vulkan_video — H.264 encode/decode via Vulkan Video (vk-video v0.3). Cross-vendor (Intel ANV, NVIDIA, AMD RADV). Includes lazy encoder creation on first frame for resolution detection, NV12/I420 input support, and configurable bitrate/framerate/keyframe interval. vaapi — AV1 encode/decode via VA-API (cros-codecs v0.0.6). Primarily Intel (intel-media-driver), also AMD. Uses GBM surfaces for zero-copy VA-API buffer management. Includes stride-aware NV12 plane read/write helpers with odd-width correctness. nvcodec — AV1 encode/decode via NVENC/NVDEC (shiguredo_nvcodec v2025.2). NVIDIA only (RTX 30xx+ decode, RTX 40xx+ AV1 encode). Dynamic CUDA loading — no build-time CUDA Toolkit required for the host binary. All backends share: - HwAccelMode enum (auto/force_hw/force_cpu) for graceful fallback - ProcessorNode trait integration with health reporting - Consistent config structs with serde deny_unknown_fields validation - Comprehensive unit tests (mock-based, no GPU required) Closes #217 Signed-off-by: Devin AI Co-Authored-By: Claudio Costa --- Cargo.lock | 552 +++++++- crates/nodes/Cargo.toml | 14 + crates/nodes/src/video/mod.rs | 43 +- crates/nodes/src/video/nv_av1.rs | 1184 ++++++++++++++++ crates/nodes/src/video/vaapi_av1.rs | 1807 ++++++++++++++++++++++++ crates/nodes/src/video/vulkan_video.rs | 1461 +++++++++++++++++++ justfile | 3 +- 7 files changed, 4998 insertions(+), 66 deletions(-) create mode 100644 crates/nodes/src/video/nv_av1.rs create mode 100644 crates/nodes/src/video/vaapi_av1.rs create mode 100644 crates/nodes/src/video/vulkan_video.rs diff --git a/Cargo.lock b/Cargo.lock index 9ba9822b..b0e6ba12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,7 +183,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -246,7 +246,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -258,7 +258,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -297,7 +297,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -308,7 +308,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -343,7 +343,7 @@ checksum = "49c98dba06b920588de7d63f6acc23f1e6a9fade5fd6198e564506334fb5a4f5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -573,6 +573,46 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn 2.0.117", +] + [[package]] name = "bit-set" version = "0.9.1" @@ -609,6 +649,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + [[package]] name = "bitstream-io" version = "4.9.0" @@ -686,7 +732,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -818,12 +864,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "cfg_aliases" version = "0.2.1" @@ -882,6 +943,17 @@ dependencies = [ "half", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + [[package]] name = "clap" version = "4.6.0" @@ -913,7 +985,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -955,6 +1027,8 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ + "serde", + "termcolor", "unicode-width", ] @@ -1334,6 +1408,40 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "cros-codecs" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f7441b4f31c17b6b6b7f57f6c202944aad11d0ab23739a9ff88d8d34dec621" +dependencies = [ + "anyhow", + "byteorder", + "crc32fast", + "cros-libva", + "drm", + "drm-fourcc", + "gbm", + "gbm-sys", + "log", + "nix 0.28.0", + "thiserror 1.0.69", + "zerocopy 0.8.47", +] + +[[package]] +name = "cros-libva" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902c9726e953b678595456bd38f95f31aaf1947c56dd9f4a2290f3f1eca4d228" +dependencies = [ + "bindgen 0.70.1", + "bitflags 2.11.0", + "log", + "pkg-config", + "regex", + "thiserror 1.0.69", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -1404,7 +1512,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.117", ] [[package]] @@ -1415,7 +1523,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1463,6 +1571,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -1482,7 +1601,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.117", "unicode-xid", ] @@ -1553,7 +1672,16 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading 0.8.9", ] [[package]] @@ -1574,6 +1702,45 @@ dependencies = [ "litrs", ] +[[package]] +name = "drm" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix 0.38.44", +] + +[[package]] +name = "drm-ffi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +dependencies = [ + "drm-sys", + "rustix 0.38.44", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" +dependencies = [ + "libc", + "linux-raw-sys 0.6.5", +] + [[package]] name = "dunce" version = "1.0.5" @@ -1654,7 +1821,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1869,6 +2036,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "four-cc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795cbfc56d419a7ce47ccbb7504dd9a5b7c484c083c356e797de08bd988d9629" + [[package]] name = "fs-err" version = "3.3.0" @@ -1952,7 +2125,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1998,6 +2171,28 @@ dependencies = [ "serde_json", ] +[[package]] +name = "gbm" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bf55ba6dd53ad0ac115046ff999c5324c283444ee6e0be82454c4e8eb2f36a" +dependencies = [ + "bitflags 2.11.0", + "drm", + "drm-fourcc", + "gbm-sys", + "libc", +] + +[[package]] +name = "gbm-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9cc2f64de9fa707b5c6b2d2f10d7a7e49e845018a9f5685891eb40d3bab2538" +dependencies = [ + "libc", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2077,6 +2272,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + [[package]] name = "glob" version = "0.3.3" @@ -2095,6 +2301,27 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glow" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + [[package]] name = "gpu-allocator" version = "0.28.0" @@ -2148,6 +2375,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "h264-reader" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "036a78b2620d92f0ec57690bc792b3bb87348632ee5225302ba2e66a48021c6c" +dependencies = [ + "bitstream-io 2.6.0", + "hex-slice", + "log", + "memchr", + "rfc6381-codec", +] + [[package]] name = "half" version = "2.7.1" @@ -2249,6 +2489,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-slice" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a308e0214554f07a81d8944abe45f552871c12e3c3c6e7e5d354039a6c4c" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -2658,7 +2904,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2803,7 +3049,7 @@ checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2878,6 +3124,23 @@ dependencies = [ "signature", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.9", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + [[package]] name = "kurbo" version = "0.13.0" @@ -2973,6 +3236,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -3210,6 +3479,21 @@ dependencies = [ "pxfm", ] +[[package]] +name = "mp4ra-rust" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdbc3d3867085d66ac6270482e66f3dd2c5a18451a3dc9ad7269e94844a536b7" +dependencies = [ + "four-cc", +] + +[[package]] +name = "mpeg4-audio-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a1fe2275b68991faded2c80aa4a33dba398b77d276038b8f50701a22e55918" + [[package]] name = "multer" version = "3.1.0" @@ -3243,7 +3527,7 @@ dependencies = [ "bit-set", "bitflags 2.11.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.2.1", "codespan-reporting", "half", "hashbrown 0.16.1", @@ -3286,6 +3570,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -3312,6 +3605,18 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", +] + [[package]] name = "nix" version = "0.30.1" @@ -3320,7 +3625,7 @@ checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.11.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.2.1", "libc", ] @@ -3420,7 +3725,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3493,7 +3798,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3695,7 +4000,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3877,7 +4182,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3919,7 +4224,7 @@ checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4069,7 +4374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -4107,7 +4412,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "version_check", "yansi", ] @@ -4128,7 +4433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4168,7 +4473,7 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn", + "syn 2.0.117", "tempfile", ] @@ -4182,7 +4487,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4195,7 +4500,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4236,7 +4541,7 @@ checksum = "823a9d8da391be21a5f4d5e11c39d15f45b011076c6825fc2323f7e4753f09ce" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4305,7 +4610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", - "cfg_aliases", + "cfg_aliases 0.2.1", "pin-project-lite", "quinn-proto", "quinn-udp", @@ -4348,7 +4653,7 @@ version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.1", "libc", "once_cell", "socket2", @@ -4511,7 +4816,7 @@ dependencies = [ "arrayvec", "av-scenechange", "av1-grain", - "bitstream-io", + "bitstream-io 4.9.0", "built", "cc", "cfg-if", @@ -4650,7 +4955,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4834,6 +5139,16 @@ dependencies = [ "usvg", ] +[[package]] +name = "rfc6381-codec" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed54c20f5c3ec82eab6d998b313dc75ec5d5650d4f57675e61d72489040297fd" +dependencies = [ + "mp4ra-rust", + "mpeg4-audio-const", +] + [[package]] name = "rgb" version = "0.8.53" @@ -4932,7 +5247,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn", + "syn 2.0.117", "walkdir", ] @@ -5234,7 +5549,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 2.0.117", ] [[package]] @@ -5323,7 +5638,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5334,7 +5649,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5419,7 +5734,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5483,6 +5798,17 @@ version = "2026.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b135058874815f8f13edae644ceedb659f7238fe4a9e2b1bdceecc72dc659b35" +[[package]] +name = "shiguredo_nvcodec" +version = "2025.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abdb7e695a3fe6f37ea08a6366c6848ea1d4491dafbf793fe5d2691928087c8" +dependencies = [ + "bindgen 0.72.1", + "libloading 0.8.9", + "toml 0.9.12+spec-1.1.0", +] + [[package]] name = "shlex" version = "1.3.0" @@ -5554,6 +5880,15 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -5705,6 +6040,7 @@ dependencies = [ "bytes", "cc", "cmake", + "cros-codecs", "env-libvpx-sys", "fontdue", "futures", @@ -5734,6 +6070,7 @@ dependencies = [ "serde-saphyr", "serde_json", "shiguredo_mp4", + "shiguredo_nvcodec", "smallvec", "streamkit-core", "symphonia", @@ -5747,6 +6084,7 @@ dependencies = [ "ts-rs", "url", "uuid", + "vk-video", "webm", "wgpu", "wildmatch", @@ -5923,7 +6261,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.117", ] [[package]] @@ -6074,6 +6412,17 @@ dependencies = [ "symphonia-metadata", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.117" @@ -6102,7 +6451,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6221,7 +6570,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6232,7 +6581,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6414,7 +6763,7 @@ checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6764,7 +7113,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -6870,7 +7219,7 @@ checksum = "38d90eea51bc7988ef9e674bf80a85ba6804739e535e9cab48e4bb34a8b652aa" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "termcolor", ] @@ -7087,7 +7436,38 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "vk-mem" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb12b79bcec57a3334d0284f1364c1846f378bb47df9779c6dbfcfc245c9404" +dependencies = [ + "ash", + "bitflags 2.11.0", + "cc", +] + +[[package]] +name = "vk-video" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6accac84fee2e209165c93dfc9e44ae37391b4e0b812aba92660bfc0ca77c440" +dependencies = [ + "ash", + "bytemuck", + "bytes", + "cfg_aliases 0.2.1", + "derivative", + "h264-reader", + "memchr", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "tracing", + "vk-mem", + "wgpu", ] [[package]] @@ -7179,7 +7559,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -7417,7 +7797,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", "wit-parser", @@ -7526,7 +7906,7 @@ checksum = "cbfbbfdb0cfd638145b0de4d3e309901ccc4e29965a33ca1eb18ab6f37057350" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -7633,6 +8013,18 @@ dependencies = [ "wast 245.0.1", ] +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + [[package]] name = "web-async" version = "0.1.3" @@ -7750,15 +8142,21 @@ dependencies = [ "bitflags 2.11.0", "bytemuck", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.2.1", "document-features", "hashbrown 0.16.1", + "js-sys", "log", + "naga", + "parking_lot", "portable-atomic", "profiling", "raw-window-handle", "smallvec", "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "wgpu-core", "wgpu-hal", "wgpu-types", @@ -7775,7 +8173,7 @@ dependencies = [ "bit-vec", "bitflags 2.11.0", "bytemuck", - "cfg_aliases", + "cfg_aliases 0.2.1", "document-features", "hashbrown 0.16.1", "indexmap 2.13.0", @@ -7790,6 +8188,7 @@ dependencies = [ "smallvec", "thiserror 2.0.18", "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-naga-bridge", @@ -7805,6 +8204,15 @@ dependencies = [ "wgpu-hal", ] +[[package]] +name = "wgpu-core-deps-emscripten" +version = "29.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef043bf135cc68b6f667c55ff4e345ce2b5924d75bad36a47921b0287ca4b24a" +dependencies = [ + "wgpu-hal", +] + [[package]] name = "wgpu-core-deps-windows-linux-android" version = "29.0.0" @@ -7828,14 +8236,19 @@ dependencies = [ "block2", "bytemuck", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.2.1", + "glow", + "glutin_wgl_sys", "gpu-allocator", "gpu-descriptor", "hashbrown 0.16.1", + "js-sys", + "khronos-egl", "libc", "libloading 0.8.9", "log", "naga", + "ndk-sys", "objc2", "objc2-core-foundation", "objc2-foundation", @@ -7853,6 +8266,9 @@ dependencies = [ "renderdoc-sys", "smallvec", "thiserror 2.0.18", + "wasm-bindgen", + "wayland-sys", + "web-sys", "wgpu-naga-bridge", "wgpu-types", "windows", @@ -7877,8 +8293,10 @@ checksum = "ec2675540fb1a5cfa5ef122d3d5f390e2c75711a0b946410f2d6ac3a0f77d1f6" dependencies = [ "bitflags 2.11.0", "bytemuck", + "js-sys", "log", "raw-window-handle", + "web-sys", ] [[package]] @@ -7914,7 +8332,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasmtime-environ", "witx", ] @@ -7927,7 +8345,7 @@ checksum = "b8f625d05adeddad85c8d5fbcd765a8ecf1b22260840a0a193125dc4ab06ac9d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wiggle-generate", ] @@ -8049,7 +8467,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -8060,7 +8478,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -8415,7 +8833,7 @@ dependencies = [ "heck", "indexmap 2.13.0", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -8431,7 +8849,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -8519,6 +8937,12 @@ dependencies = [ "rustix 1.1.4", ] +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + [[package]] name = "xmlwriter" version = "0.1.0" @@ -8565,7 +8989,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -8596,7 +9020,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -8607,7 +9031,7 @@ checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -8627,7 +9051,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -8667,7 +9091,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] diff --git a/crates/nodes/Cargo.toml b/crates/nodes/Cargo.toml index 62a2e06c..3aca760f 100644 --- a/crates/nodes/Cargo.toml +++ b/crates/nodes/Cargo.toml @@ -106,6 +106,11 @@ wgpu = { version = "29", optional = true, default-features = false, features = [ pollster = { version = "0.4", optional = true } bytemuck = { version = "1.22", optional = true, features = ["derive"] } +# HW-accelerated video codecs (optional, behind respective features) +vk-video = { version = "0.3", optional = true } # vulkan_video feature — Vulkan Video H.264 HW codec +cros-codecs = { version = "0.0.6", optional = true, features = ["vaapi"] } # vaapi feature — requires libva-dev system package +shiguredo_nvcodec = { version = "2025.2", optional = true } + futures-util = "0.3" [features] @@ -177,6 +182,15 @@ object_store = ["dep:opendal", "dep:schemars"] codegen = ["dep:ts-rs"] video = ["vp9", "av1", "openh264", "colorbars", "compositor"] +# HW-accelerated video codecs — not in `default`; each requires vendor-specific +# system libraries or drivers at runtime. +# vulkan_video: H.264 encode/decode via Vulkan Video (vk-video crate). Cross-vendor (Intel/NVIDIA/AMD). +vulkan_video = ["dep:schemars", "dep:vk-video", "dep:serde_json"] +# vaapi: AV1 encode/decode via VA-API (cros-codecs crate). Primarily Intel, also AMD. +vaapi = ["dep:schemars", "dep:cros-codecs", "dep:serde_json"] +# nvcodec: AV1 encode/decode via NVENC/NVDEC (shiguredo_nvcodec crate). NVIDIA only. +nvcodec = ["dep:schemars", "dep:shiguredo_nvcodec", "dep:serde_json"] + [[bin]] name = "generate-compositor-types" path = "src/bin/generate_compositor_types.rs" diff --git a/crates/nodes/src/video/mod.rs b/crates/nodes/src/video/mod.rs index 3e855914..6230541f 100644 --- a/crates/nodes/src/video/mod.rs +++ b/crates/nodes/src/video/mod.rs @@ -70,6 +70,29 @@ pub const AV1_CONTENT_TYPE: &str = "video/av1"; /// MIME-style content type for H.264-encoded video packets. pub const H264_CONTENT_TYPE: &str = "video/h264"; +// ── Hardware acceleration mode ─────────────────────────────────────────────── +// +// Shared across all HW-accelerated codec modules (Vulkan Video, VA-API, NVENC). + +/// Hardware acceleration mode for GPU codec nodes. +/// +/// Mirrors the compositor's `gpu_mode` pattern: auto-detect by default, +/// with explicit force options for testing and deployment. +#[cfg(any(feature = "vulkan_video", feature = "vaapi", feature = "nvcodec"))] +#[derive( + Debug, Clone, Copy, Default, serde::Serialize, serde::Deserialize, schemars::JsonSchema, +)] +#[serde(rename_all = "lowercase")] +pub enum HwAccelMode { + /// Auto-detect: use HW if available, fall back to CPU otherwise. + #[default] + Auto, + /// Force HW acceleration — fail if unavailable. + ForceHw, + /// Force CPU path — ignore available HW. + ForceCpu, +} + /// Parse a pixel format string into a [`PixelFormat`]. /// /// Accepts `"i420"`, `"nv12"`, `"rgba8"`, or `"rgba"` (case-insensitive). @@ -100,9 +123,27 @@ pub mod pixel_ops; #[cfg(feature = "compositor")] pub mod pixel_convert; -#[cfg(any(feature = "vp9", feature = "av1", feature = "svt_av1", feature = "openh264"))] +#[cfg(any( + feature = "vp9", + feature = "av1", + feature = "svt_av1", + feature = "openh264", + feature = "nvcodec", + feature = "vaapi" +))] pub(crate) mod encoder_trait; +// ── HW-accelerated codec modules ───────────────────────────────────────────── + +#[cfg(feature = "vulkan_video")] +pub mod vulkan_video; + +#[cfg(feature = "vaapi")] +pub mod vaapi_av1; + +#[cfg(feature = "nvcodec")] +pub mod nv_av1; + // ── Shared I420→NV12 conversion helpers ────────────────────────────────────── // // Used by both the rav1d decoder (av1.rs) and the C dav1d decoder (dav1d.rs). diff --git a/crates/nodes/src/video/nv_av1.rs b/crates/nodes/src/video/nv_av1.rs new file mode 100644 index 00000000..2de6c8b7 --- /dev/null +++ b/crates/nodes/src/video/nv_av1.rs @@ -0,0 +1,1184 @@ +// SPDX-FileCopyrightText: © 2025 StreamKit Contributors +// +// SPDX-License-Identifier: MPL-2.0 + +//! NVIDIA NVENC/NVDEC HW-accelerated AV1 encoder and decoder nodes. +//! +//! Uses the [`shiguredo_nvcodec`](https://crates.io/crates/shiguredo_nvcodec) +//! crate which provides Rust bindings for the NVIDIA Video Codec SDK. CUDA +//! driver API is loaded dynamically at runtime (`dlopen`) — no build-time +//! CUDA Toolkit dependency. +//! +//! This module provides: +//! - `NvAv1DecoderNode` — decodes AV1 packets to NV12 `VideoFrame`s via NVDEC +//! - `NvAv1EncoderNode` — encodes NV12 `VideoFrame`s to AV1 packets via NVENC +//! +//! Both nodes perform runtime capability detection: if no NVIDIA GPU with +//! AV1 support is found, node creation returns an error so the pipeline can +//! fall back to a CPU codec (rav1e/dav1d/SVT-AV1). +//! +//! # Feature gate +//! +//! Requires `nvcodec` feature. +//! +//! # GPU requirements +//! +//! - **AV1 decode**: NVIDIA RTX 30xx (Ampere) or newer. +//! - **AV1 encode**: NVIDIA RTX 40xx (Ada Lovelace) or newer. + +use async_trait::async_trait; +use bytes::Bytes; +use opentelemetry::global; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; +use std::sync::Arc; +use std::time::Instant; +use streamkit_core::stats::NodeStatsTracker; +use streamkit_core::types::{ + EncodedVideoFormat, Packet, PacketMetadata, PacketType, PixelFormat, RawVideoFormat, + VideoCodec, VideoFrame, VideoLayout, +}; +use streamkit_core::{ + config_helpers, get_codec_channel_capacity, packet_helpers, state_helpers, InputPin, + NodeContext, NodeRegistry, OutputPin, PinCardinality, PooledVideoData, ProcessorNode, + StreamKitError, VideoFramePool, +}; +use tokio::sync::mpsc; + +use super::encoder_trait::{self, EncodedPacket, EncoderNodeRunner, StandardVideoEncoder}; +use super::HwAccelMode; +use super::AV1_CONTENT_TYPE; + +// --------------------------------------------------------------------------- +// Decoder +// --------------------------------------------------------------------------- + +/// Configuration for the NVIDIA AV1 decoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct NvAv1DecoderConfig { + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, + /// CUDA device index (0-based). If `None`, use device 0. + pub cuda_device: Option, +} + +impl Default for NvAv1DecoderConfig { + fn default() -> Self { + Self { hw_accel: HwAccelMode::Auto, cuda_device: None } + } +} + +/// NVIDIA NVDEC AV1 decoder node. +/// +/// Accepts AV1 encoded `Binary` packets on its `"in"` pin and emits +/// decoded NV12 `VideoFrame`s on its `"out"` pin. +pub struct NvAv1DecoderNode { + config: NvAv1DecoderConfig, +} + +impl NvAv1DecoderNode { + /// Create a new decoder node with the given configuration. + /// + /// # Errors + /// + /// Returns an error if `hw_accel` is `ForceCpu` (this node only does HW). + pub fn new(config: NvAv1DecoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "NvAv1DecoderNode only supports hardware decoding; \ + use the CPU AV1 decoder (video::av1::decoder) for ForceCpu mode" + .to_string(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for NvAv1DecoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::Av1, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + })], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + async fn run(self: Box, mut context: NodeContext) -> Result<(), StreamKitError> { + let node_name = context.output_sender.node_name().to_string(); + state_helpers::emit_initializing(&context.state_tx, &node_name); + + tracing::info!("NvAv1DecoderNode starting"); + let mut input_rx = context.take_input("in")?; + let video_pool = context.video_pool.clone(); + + let meter = global::meter("skit_nodes"); + let packets_processed_counter = + meter.u64_counter("nv_av1_decoder_packets_processed").build(); + let decode_duration_histogram = meter + .f64_histogram("nv_av1_decode_duration") + .with_boundaries(streamkit_core::metrics::HISTOGRAM_BOUNDARIES_CODEC_PACKET.to_vec()) + .build(); + + let (decode_tx, mut decode_rx) = + mpsc::channel::<(Bytes, Option)>(get_codec_channel_capacity()); + let (result_tx, mut result_rx) = + mpsc::channel::>(get_codec_channel_capacity()); + + let cuda_device = self.config.cuda_device.unwrap_or(0); + let decode_task = tokio::task::spawn_blocking(move || { + let nv_config = shiguredo_nvcodec::DecoderConfig { + #[allow(clippy::cast_possible_wrap)] + device_id: cuda_device as i32, + max_display_delay: 0, // low-latency + ..shiguredo_nvcodec::DecoderConfig::default() + }; + + let mut decoder = match shiguredo_nvcodec::Decoder::new_av1(nv_config) { + Ok(d) => d, + Err(err) => { + let _ = result_tx.blocking_send(Err(format!( + "NVDEC: failed to create AV1 decoder on GPU {cuda_device}: {err}" + ))); + return; + }, + }; + + tracing::info!("NVDEC AV1 decoder created on GPU {cuda_device}"); + + while let Some((data, metadata)) = decode_rx.blocking_recv() { + if result_tx.is_closed() { + return; + } + + if data.is_empty() { + continue; + } + + let decode_start_time = Instant::now(); + + if let Err(err) = decoder.decode(&data) { + tracing::warn!("NVDEC AV1 decode error: {err}"); + let _ = + result_tx.blocking_send(Err(format!("NVDEC: AV1 decode failed: {err}"))); + continue; + } + + // Drain all decoded frames produced by this input packet. + loop { + match decoder.next_frame() { + Ok(Some(nv_frame)) => { + match copy_nvdec_frame(&nv_frame, metadata.clone(), video_pool.as_ref()) + { + Ok(frame) => { + if result_tx.blocking_send(Ok(frame)).is_err() { + return; + } + }, + Err(err) => { + let _ = result_tx.blocking_send(Err(err)); + }, + } + }, + Ok(None) => break, + Err(err) => { + tracing::warn!("NVDEC next_frame error: {err}"); + let _ = result_tx + .blocking_send(Err(format!("NVDEC: next_frame failed: {err}"))); + break; + }, + } + } + + // Record decode duration once per input packet (after the + // entire decode + drain cycle), matching the AV1 CPU decoder + // pattern in av1.rs. + decode_duration_histogram.record(decode_start_time.elapsed().as_secs_f64(), &[]); + } + + // Flush remaining frames. + if result_tx.is_closed() { + return; + } + if let Err(err) = decoder.finish() { + tracing::warn!("NVDEC finish error: {err}"); + return; + } + loop { + match decoder.next_frame() { + Ok(Some(nv_frame)) => { + match copy_nvdec_frame(&nv_frame, None, video_pool.as_ref()) { + Ok(frame) => { + if result_tx.blocking_send(Ok(frame)).is_err() { + return; + } + }, + Err(err) => { + let _ = result_tx.blocking_send(Err(err)); + }, + } + }, + Ok(None) => break, + Err(err) => { + tracing::warn!("NVDEC flush next_frame error: {err}"); + break; + }, + } + } + }); + + state_helpers::emit_running(&context.state_tx, &node_name); + + let mut stats_tracker = NodeStatsTracker::new(node_name.clone(), context.stats_tx.clone()); + let batch_size = context.batch_size; + + let decode_tx_clone = decode_tx.clone(); + let mut input_task = tokio::spawn(async move { + loop { + let Some(first_packet) = input_rx.recv().await else { + break; + }; + + let packet_batch = + packet_helpers::batch_packets_greedy(first_packet, &mut input_rx, batch_size); + + for packet in packet_batch { + if let Packet::Binary { data, metadata, .. } = packet { + if decode_tx_clone.send((data, metadata)).await.is_err() { + tracing::error!( + "NvAv1DecoderNode decode task has shut down unexpectedly" + ); + return; + } + } + } + } + tracing::info!("NvAv1DecoderNode input stream closed"); + }); + + crate::codec_utils::codec_forward_loop( + &mut context, + &mut result_rx, + &mut input_task, + decode_task, + decode_tx, + &packets_processed_counter, + &mut stats_tracker, + Packet::Video, + "NvAv1DecoderNode", + ) + .await; + + state_helpers::emit_stopped(&context.state_tx, &node_name, "input_closed"); + tracing::info!("NvAv1DecoderNode finished"); + Ok(()) + } +} + +/// Copy a decoded NV12 frame from `shiguredo_nvcodec` into a `VideoFrame`. +/// +/// The `DecodedFrame` already provides NV12 data (separate Y and interleaved +/// UV planes), so we copy them into a contiguous buffer with the canonical +/// packed NV12 layout. +fn copy_nvdec_frame( + decoded: &shiguredo_nvcodec::DecodedFrame, + metadata: Option, + video_pool: Option<&Arc>, +) -> Result { + #[allow(clippy::cast_possible_truncation)] + let width = decoded.width() as u32; + #[allow(clippy::cast_possible_truncation)] + let height = decoded.height() as u32; + + if width == 0 || height == 0 { + return Err("NVDEC produced empty frame".to_string()); + } + + let nv12_layout = VideoLayout::packed(width, height, PixelFormat::Nv12); + let mut data = video_pool.map_or_else( + || PooledVideoData::from_vec(vec![0u8; nv12_layout.total_bytes()]), + |pool| pool.get(nv12_layout.total_bytes()), + ); + let data_slice = data.as_mut_slice(); + + let nv12_planes = nv12_layout.planes(); + let y_plane = nv12_planes[0]; + let uv_plane = nv12_planes[1]; + + // Copy Y plane. + let y_src = decoded.y_plane(); + let y_src_stride = decoded.y_stride(); + let width_usize = width as usize; + let height_usize = height as usize; + + for row in 0..height_usize { + let src_start = row * y_src_stride; + let src_end = src_start + width_usize; + if src_end > y_src.len() { + return Err(format!("NVDEC Y plane too small: need {src_end}, have {}", y_src.len())); + } + let dst_start = y_plane.offset + row * y_plane.stride; + let dst_end = dst_start + width_usize; + if dst_end > data_slice.len() { + return Err("NVDEC Y plane copy overflow".to_string()); + } + data_slice[dst_start..dst_end].copy_from_slice(&y_src[src_start..src_end]); + } + + // Copy UV plane (already interleaved NV12 format from NVDEC). + let uv_src = decoded.uv_plane(); + let uv_src_stride = decoded.uv_stride(); + let chroma_h = uv_plane.height as usize; + let uv_row_bytes = uv_plane.width as usize; // NV12: ceil(width/2) interleaved UV pairs + + for row in 0..chroma_h { + let src_start = row * uv_src_stride; + let src_end = src_start + uv_row_bytes; + if src_end > uv_src.len() { + return Err(format!("NVDEC UV plane too small: need {src_end}, have {}", uv_src.len())); + } + let dst_start = uv_plane.offset + row * uv_plane.stride; + let dst_end = dst_start + uv_row_bytes; + if dst_end > data_slice.len() { + return Err("NVDEC UV plane copy overflow".to_string()); + } + data_slice[dst_start..dst_end].copy_from_slice(&uv_src[src_start..src_end]); + } + + VideoFrame::from_pooled(width, height, PixelFormat::Nv12, data, metadata) + .map_err(|e| e.to_string()) +} + +// --------------------------------------------------------------------------- +// Encoder +// --------------------------------------------------------------------------- + +/// Configuration for the NVIDIA AV1 encoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct NvAv1EncoderConfig { + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, + /// CUDA device index (0-based). If `None`, use device 0. + pub cuda_device: Option, + /// Target bitrate in bits per second. + pub bitrate: u32, + /// Target framerate in frames per second. + pub framerate: u32, + /// Keyframe interval (GOP length). `None` uses the NVENC default + /// (infinite GOP). + pub keyframe_interval: Option, +} + +impl Default for NvAv1EncoderConfig { + fn default() -> Self { + Self { + hw_accel: HwAccelMode::Auto, + cuda_device: None, + bitrate: 2_000_000, + framerate: 30, + keyframe_interval: None, + } + } +} + +/// NVIDIA NVENC AV1 encoder node. +/// +/// Accepts NV12/I420 `VideoFrame`s on its `"in"` pin and emits AV1 +/// encoded `Binary` packets on its `"out"` pin. +pub struct NvAv1EncoderNode { + config: NvAv1EncoderConfig, +} + +impl NvAv1EncoderNode { + /// Create a new encoder node with the given configuration. + /// + /// # Errors + /// + /// Returns an error if `hw_accel` is `ForceCpu` (this node only does HW). + pub fn new(config: NvAv1EncoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "NvAv1EncoderNode only supports hardware encoding; \ + use the CPU AV1 encoder (video::av1::encoder) for ForceCpu mode" + .to_string(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for NvAv1EncoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![ + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::I420, + }), + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + ], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::Av1, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + fn content_type(&self) -> Option { + Some(AV1_CONTENT_TYPE.to_string()) + } + + async fn run(self: Box, context: NodeContext) -> Result<(), StreamKitError> { + encoder_trait::run_encoder(*self, context).await + } +} + +impl EncoderNodeRunner for NvAv1EncoderNode { + const CONTENT_TYPE: &'static str = AV1_CONTENT_TYPE; + const NODE_LABEL: &'static str = "NvAv1EncoderNode"; + const PACKETS_COUNTER_NAME: &'static str = "nv_av1_encoder_packets_processed"; + const DURATION_HISTOGRAM_NAME: &'static str = "nv_av1_encode_duration"; + + fn spawn_codec_task( + self, + encode_rx: mpsc::Receiver<(VideoFrame, Option)>, + result_tx: mpsc::Sender>, + duration_histogram: opentelemetry::metrics::Histogram, + ) -> tokio::task::JoinHandle<()> { + encoder_trait::spawn_standard_encode_task::( + self.config, + encode_rx, + result_tx, + duration_histogram, + ) + } +} + +// --------------------------------------------------------------------------- +// Internal NVENC wrapper implementing StandardVideoEncoder +// --------------------------------------------------------------------------- + +struct NvAv1Encoder { + encoder: shiguredo_nvcodec::Encoder, + next_pts: i64, +} + +impl StandardVideoEncoder for NvAv1Encoder { + type Config = NvAv1EncoderConfig; + const CODEC_NAME: &'static str = "NV-AV1"; + + fn new_encoder(width: u32, height: u32, config: &Self::Config) -> Result + where + Self: Sized, + { + let cuda_device = config.cuda_device.unwrap_or(0); + + let nv_config = shiguredo_nvcodec::EncoderConfig { + width, + height, + fps_numerator: config.framerate, + fps_denominator: 1, + target_bitrate: Some(config.bitrate), + preset: shiguredo_nvcodec::Preset::P1, // fastest for real-time + tuning_info: shiguredo_nvcodec::TuningInfo::LOW_LATENCY, + rate_control_mode: shiguredo_nvcodec::RateControlMode::Cbr, + gop_length: config.keyframe_interval, + idr_period: config.keyframe_interval, + frame_interval_p: 1, // no B-frames for low latency + profile: None, + #[allow(clippy::cast_possible_wrap)] + device_id: cuda_device as i32, + max_encode_width: None, + max_encode_height: None, + }; + + let encoder = shiguredo_nvcodec::Encoder::new_av1(nv_config).map_err(|err| { + format!("NVENC: failed to create AV1 encoder on GPU {cuda_device}: {err}") + })?; + + tracing::info!( + width, + height, + bitrate = config.bitrate, + framerate = config.framerate, + gpu = cuda_device, + "NVENC AV1 encoder created" + ); + + Ok(Self { encoder, next_pts: 0 }) + } + + fn encode( + &mut self, + frame: &VideoFrame, + metadata: Option, + ) -> Result, String> { + let nv12_data = match frame.pixel_format { + PixelFormat::Nv12 => Cow::Borrowed(frame.data.as_slice()), + PixelFormat::I420 => Cow::Owned(i420_to_nv12_buffer(frame)), + other => { + return Err(format!("NV-AV1 encoder expects NV12 or I420 input, got {other:?}")); + }, + }; + + self.encoder + .encode(&nv12_data) + .map_err(|err| format!("NVENC: AV1 encode failed: {err}"))?; + + Ok(self.drain_packets(metadata)) + } + + fn flush_encoder(&mut self) -> Result, String> { + self.encoder.finish().map_err(|err| format!("NVENC: AV1 finish failed: {err}"))?; + + Ok(self.drain_packets(None)) + } + + fn flush_on_dimension_change() -> bool { + true + } +} + +impl NvAv1Encoder { + /// Drain all available encoded frames from NVENC. + fn drain_packets(&mut self, metadata: Option) -> Vec { + let mut packets = Vec::new(); + let mut remaining_metadata = metadata; + + loop { + let Some(encoded) = self.encoder.next_frame() else { + break; + }; + + let is_keyframe = matches!( + encoded.picture_type(), + shiguredo_nvcodec::PictureType::I | shiguredo_nvcodec::PictureType::Idr + ); + let data = Bytes::from(encoded.into_data()); + + let pts = self.next_pts; + self.next_pts += 1; + + let meta = remaining_metadata.take(); + let output_metadata = merge_keyframe_metadata(meta, is_keyframe, pts); + + packets.push(EncodedPacket { data, metadata: Some(output_metadata) }); + } + + packets + } +} + +/// Convert an I420 `VideoFrame` to a contiguous NV12 byte buffer suitable +/// for `shiguredo_nvcodec::Encoder::encode()`. +fn i420_to_nv12_buffer(frame: &VideoFrame) -> Vec { + let width = frame.width as usize; + let height = frame.height as usize; + let layout = frame.layout(); + let planes = layout.planes(); + let data = frame.data.as_slice(); + + // NV12 layout: Y plane (width * height) + UV plane (chroma_w*2 * chroma_h) + let chroma_w = width.div_ceil(2); + let chroma_h = height.div_ceil(2); + let uv_row_bytes = chroma_w * 2; // ceil(width/2) pairs of (U, V) + let nv12_size = width * height + uv_row_bytes * chroma_h; + let mut nv12 = vec![0u8; nv12_size]; + + // Copy Y plane. + let y_plane = &planes[0]; + for row in 0..height { + let src_start = y_plane.offset + row * y_plane.stride; + let dst_start = row * width; + nv12[dst_start..dst_start + width].copy_from_slice(&data[src_start..src_start + width]); + } + + // Interleave U + V into NV12 UV plane. + let u_plane = &planes[1]; + let v_plane = &planes[2]; + let uv_offset = width * height; + + for row in 0..chroma_h { + let u_src_start = u_plane.offset + row * u_plane.stride; + let v_src_start = v_plane.offset + row * v_plane.stride; + let dst_start = uv_offset + row * uv_row_bytes; + for col in 0..chroma_w { + nv12[dst_start + col * 2] = data[u_src_start + col]; + nv12[dst_start + col * 2 + 1] = data[v_src_start + col]; + } + } + + nv12 +} + +#[allow(clippy::missing_const_for_fn)] // map_or with closures is not yet stable in const fn +fn merge_keyframe_metadata( + metadata: Option, + keyframe: bool, + pts: i64, +) -> PacketMetadata { + metadata.map_or( + PacketMetadata { + #[allow(clippy::cast_sign_loss)] + timestamp_us: if pts >= 0 { Some(pts as u64) } else { None }, + duration_us: None, + sequence: None, + keyframe: Some(keyframe), + }, + |meta| PacketMetadata { + timestamp_us: meta.timestamp_us, + duration_us: meta.duration_us, + sequence: meta.sequence, + keyframe: Some(keyframe), + }, + ) +} + +// --------------------------------------------------------------------------- +// Registration +// --------------------------------------------------------------------------- + +use schemars::schema_for; +use streamkit_core::registry::StaticPins; + +#[allow(clippy::expect_used, clippy::missing_panics_doc)] +pub fn register_nv_av1_nodes(registry: &mut NodeRegistry) { + // Runtime capability check: verify that CUDA libraries are loadable. + // If not, log a warning but still register the nodes — they will fail + // at runtime with a clear error when the pipeline starts. + if !shiguredo_nvcodec::is_cuda_library_available() { + tracing::warn!( + "CUDA libraries not available — NV AV1 encoder/decoder nodes \ + will fail at runtime if used" + ); + } + + let default_decoder = NvAv1DecoderNode::new(NvAv1DecoderConfig::default()) + .expect("default NV AV1 decoder config should be valid"); + registry.register_static_with_description( + "video::nv::av1_decoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(NvAv1DecoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(NvAv1DecoderConfig)) + .expect("NvAv1DecoderConfig schema should serialize to JSON"), + StaticPins { inputs: default_decoder.input_pins(), outputs: default_decoder.output_pins() }, + vec![ + "video".to_string(), + "codecs".to_string(), + "av1".to_string(), + "hw".to_string(), + "nvidia".to_string(), + ], + false, + "Decodes AV1-compressed packets into raw NV12 video frames using \ + NVIDIA NVDEC hardware acceleration. Requires an NVIDIA RTX 30xx \ + (Ampere) or newer GPU.", + ); + + let default_encoder = NvAv1EncoderNode::new(NvAv1EncoderConfig::default()) + .expect("default NV AV1 encoder config should be valid"); + registry.register_static_with_description( + "video::nv::av1_encoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(NvAv1EncoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(NvAv1EncoderConfig)) + .expect("NvAv1EncoderConfig schema should serialize to JSON"), + StaticPins { inputs: default_encoder.input_pins(), outputs: default_encoder.output_pins() }, + vec![ + "video".to_string(), + "codecs".to_string(), + "av1".to_string(), + "hw".to_string(), + "nvidia".to_string(), + ], + false, + "Encodes raw video frames (NV12 or I420) into AV1 packets using \ + NVIDIA NVENC hardware acceleration. Requires an NVIDIA RTX 40xx \ + (Ada Lovelace) or newer GPU. Insert a video::pixel_convert node \ + upstream if the source outputs RGBA8.", + ); +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::disallowed_macros)] +mod tests { + use super::*; + use crate::test_utils::{ + assert_state_initializing, assert_state_running, assert_state_stopped, create_test_context, + create_test_video_frame, + }; + use std::borrow::Cow; + use std::collections::HashMap; + use tokio::sync::mpsc; + + // ── Helpers ───────────────────────────────────────────────────────────── + + /// Returns `true` if CUDA libraries can be loaded AND a decoder can + /// actually be created on device 0. This catches machines that have + /// `libcuda.so` but no physical GPU (or no AV1-capable GPU). + fn nvdec_av1_available() -> bool { + if !shiguredo_nvcodec::is_cuda_library_available() { + return false; + } + let config = shiguredo_nvcodec::DecoderConfig { + device_id: 0, + max_display_delay: 0, + ..shiguredo_nvcodec::DecoderConfig::default() + }; + shiguredo_nvcodec::Decoder::new_av1(config).is_ok() + } + + /// Returns `true` if NVENC AV1 encoding is available on device 0. + /// AV1 encode requires RTX 40xx (Ada Lovelace) or newer. + fn nvenc_av1_available() -> bool { + if !shiguredo_nvcodec::is_cuda_library_available() { + return false; + } + let config = shiguredo_nvcodec::EncoderConfig { + width: 64, + height: 64, + fps_numerator: 30, + fps_denominator: 1, + target_bitrate: Some(2_000_000), + preset: shiguredo_nvcodec::Preset::P1, + tuning_info: shiguredo_nvcodec::TuningInfo::LOW_LATENCY, + rate_control_mode: shiguredo_nvcodec::RateControlMode::Cbr, + gop_length: Some(1), + idr_period: Some(1), + frame_interval_p: 1, + profile: None, + device_id: 0, + max_encode_width: None, + max_encode_height: None, + }; + shiguredo_nvcodec::Encoder::new_av1(config).is_ok() + } + + // ── Unit tests (no GPU required) ──────────────────────────────────────── + + #[test] + fn force_cpu_decoder_rejected() { + let result = NvAv1DecoderNode::new(NvAv1DecoderConfig { + hw_accel: HwAccelMode::ForceCpu, + cuda_device: None, + }); + assert!(result.is_err(), "ForceCpu should be rejected by NV decoder"); + } + + #[test] + fn force_cpu_encoder_rejected() { + let result = NvAv1EncoderNode::new(NvAv1EncoderConfig { + hw_accel: HwAccelMode::ForceCpu, + cuda_device: None, + bitrate: 2_000_000, + keyframe_interval: None, + }); + assert!(result.is_err(), "ForceCpu should be rejected by NV encoder"); + } + + #[test] + fn default_configs_accepted() { + assert!(NvAv1DecoderNode::new(NvAv1DecoderConfig::default()).is_ok()); + assert!(NvAv1EncoderNode::new(NvAv1EncoderConfig::default()).is_ok()); + } + + #[test] + fn decoder_pins_correct() { + let node = NvAv1DecoderNode::new(NvAv1DecoderConfig::default()).unwrap(); + let inputs = node.input_pins(); + let outputs = node.output_pins(); + assert_eq!(inputs.len(), 1); + assert_eq!(outputs.len(), 1); + assert_eq!(inputs[0].name, "in"); + assert_eq!(outputs[0].name, "out"); + assert!( + matches!(&inputs[0].accepts_types[0], PacketType::EncodedVideo(fmt) if fmt.codec == VideoCodec::Av1), + "Decoder input should accept AV1" + ); + assert!( + matches!(&outputs[0].produces_type, PacketType::RawVideo(fmt) if fmt.pixel_format == PixelFormat::Nv12), + "Decoder output should produce NV12" + ); + } + + #[test] + fn encoder_pins_correct() { + let node = NvAv1EncoderNode::new(NvAv1EncoderConfig::default()).unwrap(); + let inputs = node.input_pins(); + let outputs = node.output_pins(); + assert_eq!(inputs.len(), 1); + assert_eq!(outputs.len(), 1); + assert_eq!(inputs[0].name, "in"); + assert_eq!(outputs[0].name, "out"); + // Encoder should accept both I420 and NV12. + assert_eq!(inputs[0].accepts_types.len(), 2); + assert!( + matches!(&outputs[0].produces_type, PacketType::EncodedVideo(fmt) if fmt.codec == VideoCodec::Av1), + "Encoder output should produce AV1" + ); + } + + #[test] + fn deny_unknown_fields_decoder() { + let json = r#"{"hw_accel":"Auto","cuda_device":null,"bogus_field":42}"#; + let result: Result = serde_json::from_str(json); + assert!(result.is_err(), "Unknown fields should be rejected"); + } + + #[test] + fn deny_unknown_fields_encoder() { + let json = r#"{"bitrate":1000000,"unknown_key":"oops"}"#; + let result: Result = serde_json::from_str(json); + assert!(result.is_err(), "Unknown fields should be rejected"); + } + + #[test] + fn i420_to_nv12_basic() { + // Build a minimal 4×4 I420 frame and convert. + let frame = create_test_video_frame(4, 4, PixelFormat::I420, 1); + let nv12 = i420_to_nv12_buffer(&frame); + + // NV12 size: Y (4*4) + UV (ceil(4/2)*2 * ceil(4/2)) = 16 + 4*2 = 24 + let expected_size = 4 * 4 + 4 * 2; + assert_eq!(nv12.len(), expected_size, "NV12 buffer size mismatch"); + } + + // ── GPU integration tests ─────────────────────────────────────────────── + + /// Encode several NV12 frames via NVENC, then decode them via NVDEC. + /// This is the full HW roundtrip test. + #[tokio::test] + async fn gpu_tests_nv_av1_encode_decode_roundtrip() { + if !nvenc_av1_available() { + eprintln!("Skipping NV AV1 encode/decode roundtrip: NVENC AV1 not available"); + return; + } + if !nvdec_av1_available() { + eprintln!("Skipping NV AV1 encode/decode roundtrip: NVDEC AV1 not available"); + return; + } + + // --- Encode --- + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder_config = NvAv1EncoderConfig { + bitrate: 2_000_000, + keyframe_interval: Some(1), + ..Default::default() + }; + let encoder = NvAv1EncoderNode::new(encoder_config).unwrap(); + + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + for index in 0_u64..5 { + let timestamp = 1_000 + 33_333_u64 * index; + let duration: u64 = 33_333; + + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(timestamp), + duration_us: Some(duration), + sequence: Some(index), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty(), "NVENC AV1 encoder produced no packets"); + + // --- Decode --- + let (dec_input_tx, dec_input_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_input_rx); + + let (dec_context, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = NvAv1DecoderNode::new(NvAv1DecoderConfig::default()).unwrap(); + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_context).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + for packet in encoded_packets { + if let Packet::Binary { data, metadata, .. } = packet { + dec_input_tx + .send(Packet::Binary { + data, + content_type: Some(Cow::Borrowed(AV1_CONTENT_TYPE)), + metadata, + }) + .await + .unwrap(); + } + } + drop(dec_input_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "NVDEC AV1 decoder produced no frames"); + + for packet in decoded_packets { + match packet { + Packet::Video(frame) => { + assert_eq!(frame.width, 64); + assert_eq!(frame.height, 64); + assert_eq!(frame.pixel_format, PixelFormat::Nv12); + assert!(!frame.data().is_empty(), "Decoded frame should have data"); + }, + _ => panic!("Expected Video packet from NV AV1 decoder"), + } + } + } + + /// Encode-only test: verify that the encoder produces output packets + /// and that the first packet is marked as a keyframe. + #[tokio::test] + async fn gpu_tests_nv_av1_encoder_produces_keyframes() { + if !nvenc_av1_available() { + eprintln!("Skipping NV AV1 encoder keyframe test: NVENC AV1 not available"); + return; + } + + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder_config = NvAv1EncoderConfig { + bitrate: 2_000_000, + keyframe_interval: Some(1), + ..Default::default() + }; + let encoder = NvAv1EncoderNode::new(encoder_config).unwrap(); + + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + for index in 0_u64..3 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(33_333 * index), + duration_us: Some(33_333), + sequence: Some(index), + keyframe: None, + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty(), "NVENC AV1 encoder produced no packets"); + + // With keyframe_interval=1, every packet should be a keyframe. + for (i, packet) in encoded_packets.iter().enumerate() { + if let Packet::Binary { metadata, .. } = packet { + let meta = metadata.as_ref().expect("Encoded packet should have metadata"); + assert_eq!( + meta.keyframe, + Some(true), + "Packet {i} should be a keyframe with keyframe_interval=1" + ); + } + } + } + + /// Encode from I420 input — verifies the I420→NV12 conversion path. + #[tokio::test] + async fn gpu_tests_nv_av1_encoder_i420_input() { + if !nvenc_av1_available() { + eprintln!("Skipping NV AV1 I420 input test: NVENC AV1 not available"); + return; + } + + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder_config = NvAv1EncoderConfig { + bitrate: 2_000_000, + keyframe_interval: Some(1), + ..Default::default() + }; + let encoder = NvAv1EncoderNode::new(encoder_config).unwrap(); + + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + // Send I420 frames instead of NV12. + for index in 0_u64..3 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::I420, 1); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(33_333 * index), + duration_us: Some(33_333), + sequence: Some(index), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!( + !encoded_packets.is_empty(), + "NVENC AV1 encoder produced no packets from I420 input" + ); + } + + /// Metadata propagation: timestamps from input frames should be + /// preserved through the encode→decode roundtrip. + #[tokio::test] + async fn gpu_tests_nv_av1_metadata_propagation() { + if !nvenc_av1_available() || !nvdec_av1_available() { + eprintln!("Skipping NV AV1 metadata test: NVENC/NVDEC AV1 not available"); + return; + } + + // --- Encode --- + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder_config = NvAv1EncoderConfig { + bitrate: 2_000_000, + keyframe_interval: Some(1), + ..Default::default() + }; + let encoder = NvAv1EncoderNode::new(encoder_config).unwrap(); + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + let timestamps: Vec = vec![1_000, 34_333, 67_666]; + for (i, &ts) in timestamps.iter().enumerate() { + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(ts), + duration_us: Some(33_333), + sequence: Some(i as u64), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty()); + + // --- Decode and verify metadata --- + let (dec_input_tx, dec_input_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_input_rx); + + let (dec_context, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = NvAv1DecoderNode::new(NvAv1DecoderConfig::default()).unwrap(); + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_context).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + for packet in encoded_packets { + if let Packet::Binary { data, metadata, .. } = packet { + dec_input_tx + .send(Packet::Binary { + data, + content_type: Some(Cow::Borrowed(AV1_CONTENT_TYPE)), + metadata, + }) + .await + .unwrap(); + } + } + drop(dec_input_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "Decoder should produce at least one frame"); + + // Every decoded frame should have metadata preserved. + for (i, packet) in decoded_packets.iter().enumerate() { + match packet { + Packet::Video(frame) => { + assert!(frame.metadata.is_some(), "Decoded frame {i} should have metadata"); + }, + _ => panic!("Expected Video packet from NV AV1 decoder"), + } + } + } +} diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs new file mode 100644 index 00000000..2d1be2bb --- /dev/null +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -0,0 +1,1807 @@ +// SPDX-FileCopyrightText: © 2025 StreamKit Contributors +// +// SPDX-License-Identifier: MPL-2.0 + +//! VA-API HW-accelerated AV1 encoder and decoder nodes. +//! +//! Uses the [`cros-codecs`](https://crates.io/crates/cros-codecs) crate which +//! provides high-level VA-API AV1 codec abstractions on Linux. The cros-codecs +//! `StatelessDecoder` and `StatelessEncoder` handle all AV1 bitstream parsing +//! and VA-API parameter buffer construction internally — this module manages +//! frame I/O and integrates with StreamKit's pipeline architecture. +//! +//! # Nodes +//! +//! - [`VaapiAv1DecoderNode`] — decodes AV1 OBU packets to NV12 [`VideoFrame`]s +//! - [`VaapiAv1EncoderNode`] — encodes NV12/I420 [`VideoFrame`]s to AV1 packets +//! +//! Both perform runtime capability detection: if no VA-API device is found (or +//! AV1 is not supported), node creation returns an error so the pipeline can +//! fall back to a CPU codec (rav1e/dav1d/SVT-AV1). +//! +//! # Feature gate +//! +//! Requires `vaapi` Cargo feature and `libva-dev` + `libgbm-dev` system packages. +//! +//! # Platform support +//! +//! - **Intel**: Full AV1 encode (Arc+) and decode via `intel-media-driver`. +//! - **AMD**: AV1 encode + decode via Mesa RadeonSI VA-API. +//! - **NVIDIA**: Decode only via community `nvidia-vaapi-driver` (no VA-API encoding). + +use std::rc::Rc; +use std::sync::Arc; +use std::time::Instant; + +use async_trait::async_trait; +use bytes::Bytes; +use opentelemetry::global; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use streamkit_core::stats::NodeStatsTracker; +use streamkit_core::types::{ + EncodedVideoFormat, Packet, PacketMetadata, PacketType, PixelFormat, RawVideoFormat, + VideoCodec, VideoFrame, +}; +use streamkit_core::{ + config_helpers, get_codec_channel_capacity, packet_helpers, state_helpers, InputPin, + NodeContext, NodeRegistry, OutputPin, PinCardinality, ProcessorNode, StreamKitError, +}; +use tokio::sync::mpsc; + +// cros-codecs high-level APIs. +use cros_codecs::backend::vaapi::decoder::VaapiBackend as VaapiDecBackend; +use cros_codecs::codec::av1::parser::Profile as Av1Profile; +use cros_codecs::decoder::stateless::av1::Av1; +use cros_codecs::decoder::stateless::{DecodeError, StatelessDecoder, StatelessVideoDecoder}; +use cros_codecs::decoder::{BlockingMode, DecodedHandle, DecoderEvent}; +use cros_codecs::encoder::av1::EncoderConfig as CrosEncoderConfig; +use cros_codecs::encoder::stateless::StatelessEncoder; +use cros_codecs::encoder::{ + FrameMetadata as CrosFrameMetadata, PredictionStructure, RateControl, Tunings, VideoEncoder, +}; +use cros_codecs::libva; +use cros_codecs::video_frame::gbm_video_frame::{ + GbmDevice, GbmExternalBufferDescriptor, GbmUsage, GbmVideoFrame, +}; +use cros_codecs::video_frame::{ReadMapping, VideoFrame as CrosVideoFrame, WriteMapping}; +use cros_codecs::{Fourcc as CrosFourcc, FrameLayout, PlaneLayout, Resolution as CrosResolution}; + +use super::encoder_trait::{self, EncodedPacket, EncoderNodeRunner, StandardVideoEncoder}; +use super::HwAccelMode; +use super::AV1_CONTENT_TYPE; + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +/// Default VA-API render device path. +const DEFAULT_RENDER_DEVICE: &str = "/dev/dri/renderD128"; + +/// AV1 superblock size — coded resolution must be aligned to this. +const AV1_SB_SIZE: u32 = 64; + +/// Maximum number of consecutive retries when the decoder returns +/// `CheckEvents` or `NotEnoughOutputBuffers` without making progress. +/// Matches the established pattern in `av1.rs` and `dav1d.rs`. +const MAX_EAGAIN_EMPTY_RETRIES: u32 = 1000; + +/// After this many retries, switch from `thread::yield_now()` to +/// `thread::sleep(1ms)` to avoid a tight spin-loop. +const EAGAIN_YIELD_THRESHOLD: u32 = 10; + +/// Default constant-quality parameter (0–255, lower = better quality). +const DEFAULT_QUALITY: u32 = 128; + +/// Default framerate for rate-control hints. +const DEFAULT_FRAMERATE: u32 = 30; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +/// NV12 fourcc code for GBM/VA-API surfaces. +fn nv12_fourcc() -> CrosFourcc { + CrosFourcc::from(b"NV12") +} + +/// Align `value` up to the next multiple of `alignment`. +fn align_up_u32(value: u32, alignment: u32) -> u32 { + debug_assert!(alignment > 0); + value.div_ceil(alignment) * alignment +} + +/// Auto-detect a VA-API render device by scanning `/dev/dri/renderD*`. +/// +/// Returns the first device path that can be opened as a VA display, or `None` +/// if no VA-API capable device is found. +fn detect_render_device() -> Option { + let mut entries: Vec<_> = std::fs::read_dir("/dev/dri") + .ok()? + .filter_map(std::result::Result::ok) + .filter(|e| e.file_name().to_str().is_some_and(|n| n.starts_with("renderD"))) + .collect(); + entries.sort_by_key(std::fs::DirEntry::file_name); + + for entry in entries { + let path = entry.path(); + if libva::Display::open_drm_display(&path).is_ok() { + return path.to_str().map(String::from); + } + } + + None +} + +/// Resolve the render device path from config, auto-detection, or default. +fn resolve_render_device(configured: Option<&String>) -> String { + if let Some(path) = configured { + return path.clone(); + } + + if let Some(path) = detect_render_device() { + tracing::info!(device = %path, "auto-detected VA-API render device"); + return path; + } + + tracing::info!( + device = DEFAULT_RENDER_DEVICE, + "no VA-API device detected, falling back to default" + ); + DEFAULT_RENDER_DEVICE.to_string() +} + +/// Open a VA display and a GBM device on the same render node. +fn open_va_and_gbm( + render_device: Option<&String>, +) -> Result<(Rc, Arc, String), String> { + let path = resolve_render_device(render_device); + let display = libva::Display::open_drm_display(&path) + .map_err(|e| format!("failed to open VA display on {path}: {e}"))?; + let gbm = + GbmDevice::open(&path).map_err(|e| format!("failed to open GBM device on {path}: {e}"))?; + Ok((display, gbm, path)) +} + +/// Copy NV12 plane data from a GBM read-mapping into a flat `Vec` suitable +/// for a packed StreamKit [`VideoFrame`]. +/// +/// Handles stride != width by copying row-by-row. +fn read_nv12_from_mapping( + mapping: &dyn ReadMapping<'_>, + width: u32, + height: u32, + plane_pitches: &[usize], +) -> Vec { + let planes = mapping.get(); + let w = width as usize; + let h = height as usize; + let y_size = w * h; + let uv_h = h.div_ceil(2); + // NV12 UV row width: interleaved U/V pairs, matching VideoLayout::packed. + let chroma_w = w.div_ceil(2) * 2; + let uv_size = chroma_w * uv_h; + let mut data = vec![0u8; y_size + uv_size]; + + // Y plane. + if !planes.is_empty() { + let y_stride = plane_pitches.first().copied().unwrap_or(w); + if y_stride == w { + let copy_len = y_size.min(planes[0].len()); + data[..copy_len].copy_from_slice(&planes[0][..copy_len]); + } else { + for row in 0..h { + let dst_off = row * w; + let src_off = row * y_stride; + if src_off + w <= planes[0].len() && dst_off + w <= y_size { + data[dst_off..dst_off + w].copy_from_slice(&planes[0][src_off..src_off + w]); + } + } + } + } + + // UV plane (interleaved). + if planes.len() > 1 { + let uv_stride = plane_pitches.get(1).copied().unwrap_or(chroma_w); + if uv_stride == chroma_w { + let copy_len = uv_size.min(planes[1].len()); + data[y_size..y_size + copy_len].copy_from_slice(&planes[1][..copy_len]); + } else { + for row in 0..uv_h { + let dst_off = y_size + row * chroma_w; + let src_off = row * uv_stride; + if src_off + chroma_w <= planes[1].len() && dst_off + chroma_w <= data.len() { + data[dst_off..dst_off + chroma_w] + .copy_from_slice(&planes[1][src_off..src_off + chroma_w]); + } + } + } + } + + data +} + +/// Write NV12 data from a StreamKit [`VideoFrame`] into a GBM frame's +/// write-mapping. +/// +/// If the source is I420, it is converted to NV12 on the fly (U/V planes +/// are interleaved into a single UV plane). +fn write_nv12_to_mapping( + mapping: &dyn WriteMapping<'_>, + frame: &VideoFrame, + plane_pitches: &[usize], +) -> Result<(), String> { + let planes = mapping.get(); + if planes.is_empty() { + return Err("GBM mapping returned no planes".into()); + } + + let w = frame.width as usize; + let h = frame.height as usize; + let src = frame.data.as_ref().as_ref(); + + match frame.pixel_format { + PixelFormat::Nv12 => { + let y_size = w * h; + // NV12 UV row width: interleaved U/V pairs, matching VideoLayout::packed. + let chroma_w = w.div_ceil(2) * 2; + let uv_h = h.div_ceil(2); + let uv_size = chroma_w * uv_h; + + // Y plane. + let y_stride = plane_pitches.first().copied().unwrap_or(w); + { + let mut y_plane = planes[0].borrow_mut(); + if y_stride == w { + let n = y_size.min(y_plane.len()).min(src.len()); + y_plane[..n].copy_from_slice(&src[..n]); + } else { + for row in 0..h { + let s = row * w; + let d = row * y_stride; + if s + w <= src.len() && d + w <= y_plane.len() { + y_plane[d..d + w].copy_from_slice(&src[s..s + w]); + } + } + } + } + + // UV plane. + if planes.len() > 1 { + let uv_stride = plane_pitches.get(1).copied().unwrap_or(chroma_w); + let mut uv_plane = planes[1].borrow_mut(); + let src_uv = &src[y_size..]; + if uv_stride == chroma_w { + let n = uv_size.min(uv_plane.len()).min(src_uv.len()); + uv_plane[..n].copy_from_slice(&src_uv[..n]); + } else { + for row in 0..uv_h { + let s = row * chroma_w; + let d = row * uv_stride; + if s + chroma_w <= src_uv.len() && d + chroma_w <= uv_plane.len() { + uv_plane[d..d + chroma_w].copy_from_slice(&src_uv[s..s + chroma_w]); + } + } + } + } + }, + PixelFormat::I420 => { + // Convert I420 → NV12: Y stays the same, U and V are interleaved. + let y_size = w * h; + let uv_w = w.div_ceil(2); + let uv_h = h.div_ceil(2); + let u_plane_size = uv_w * uv_h; + + // Y plane. + let y_stride = plane_pitches.first().copied().unwrap_or(w); + { + let mut y_plane = planes[0].borrow_mut(); + if y_stride == w { + let n = y_size.min(y_plane.len()).min(src.len()); + y_plane[..n].copy_from_slice(&src[..n]); + } else { + for row in 0..h { + let s = row * w; + let d = row * y_stride; + if s + w <= src.len() && d + w <= y_plane.len() { + y_plane[d..d + w].copy_from_slice(&src[s..s + w]); + } + } + } + } + + // UV plane — interleave U and V from I420 into NV12 UV. + if planes.len() > 1 { + let uv_stride = plane_pitches.get(1).copied().unwrap_or(uv_w * 2); + let mut uv_plane = planes[1].borrow_mut(); + for row in 0..uv_h { + for col in 0..uv_w { + let u_idx = y_size + row * uv_w + col; + let v_idx = y_size + u_plane_size + row * uv_w + col; + let dst_idx = row * uv_stride + col * 2; + if u_idx < src.len() && v_idx < src.len() && dst_idx + 1 < uv_plane.len() { + uv_plane[dst_idx] = src[u_idx]; + uv_plane[dst_idx + 1] = src[v_idx]; + } + } + } + } + }, + _ => { + return Err(format!( + "VA-API AV1 encoder requires NV12 or I420 input, got {:?}", + frame.pixel_format + )); + }, + } + + Ok(()) +} + +// --------------------------------------------------------------------------- +// Decoder +// --------------------------------------------------------------------------- + +/// Configuration for the VA-API AV1 hardware decoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct VaapiAv1DecoderConfig { + /// Path to the DRM render device (e.g. `/dev/dri/renderD128`). + /// When `None`, auto-detects the first VA-API capable device. + pub render_device: Option, + + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, +} + +impl Default for VaapiAv1DecoderConfig { + fn default() -> Self { + Self { render_device: None, hw_accel: HwAccelMode::Auto } + } +} + +pub struct VaapiAv1DecoderNode { + config: VaapiAv1DecoderConfig, +} + +impl VaapiAv1DecoderNode { + #[allow(clippy::missing_errors_doc)] + pub fn new(config: VaapiAv1DecoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "VaapiAv1DecoderNode only supports hardware decoding; \ + use video::av1::decoder for CPU decode" + .into(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for VaapiAv1DecoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::Av1, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + })], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + async fn run(self: Box, mut context: NodeContext) -> Result<(), StreamKitError> { + let node_name = context.output_sender.node_name().to_string(); + state_helpers::emit_initializing(&context.state_tx, &node_name); + + tracing::info!("VaapiAv1DecoderNode starting"); + let mut input_rx = context.take_input("in")?; + + let meter = global::meter("skit_nodes"); + let packets_processed_counter = + meter.u64_counter("vaapi_av1_decoder_packets_processed").build(); + let decode_duration_histogram = meter + .f64_histogram("vaapi_av1_decode_duration") + .with_boundaries(streamkit_core::metrics::HISTOGRAM_BOUNDARIES_CODEC_PACKET.to_vec()) + .build(); + + let (decode_tx, decode_rx) = + mpsc::channel::<(Bytes, Option)>(get_codec_channel_capacity()); + let (result_tx, mut result_rx) = + mpsc::channel::>(get_codec_channel_capacity()); + + let render_device = self.config.render_device.clone(); + let decode_task = tokio::task::spawn_blocking(move || { + vaapi_av1_decode_loop( + render_device.as_ref(), + decode_rx, + &result_tx, + &decode_duration_histogram, + ); + }); + + state_helpers::emit_running(&context.state_tx, &node_name); + + let mut stats_tracker = NodeStatsTracker::new(node_name.clone(), context.stats_tx.clone()); + let batch_size = context.batch_size; + + let decode_tx_clone = decode_tx.clone(); + let mut input_task = tokio::spawn(async move { + loop { + let Some(first_packet) = input_rx.recv().await else { + break; + }; + + let packet_batch = + packet_helpers::batch_packets_greedy(first_packet, &mut input_rx, batch_size); + + for packet in packet_batch { + if let Packet::Binary { data, metadata, .. } = packet { + if decode_tx_clone.send((data, metadata)).await.is_err() { + tracing::error!( + "VaapiAv1DecoderNode decode task has shut down unexpectedly" + ); + return; + } + } + } + } + tracing::info!("VaapiAv1DecoderNode input stream closed"); + }); + + crate::codec_utils::codec_forward_loop( + &mut context, + &mut result_rx, + &mut input_task, + decode_task, + decode_tx, + &packets_processed_counter, + &mut stats_tracker, + Packet::Video, + "VaapiAv1DecoderNode", + ) + .await; + + state_helpers::emit_stopped(&context.state_tx, &node_name, "input_closed"); + tracing::info!("VaapiAv1DecoderNode finished"); + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// Decoder — blocking decode loop +// --------------------------------------------------------------------------- + +/// Blocking decode loop running inside `spawn_blocking`. +/// +/// Creates the VA-API display, GBM device, and cros-codecs `StatelessDecoder`, +/// then processes input packets until the channel is closed. +fn vaapi_av1_decode_loop( + render_device: Option<&String>, + mut decode_rx: mpsc::Receiver<(Bytes, Option)>, + result_tx: &mpsc::Sender>, + duration_histogram: &opentelemetry::metrics::Histogram, +) { + // ── Open GBM device + VA display ────────────────────────────────── + let path = resolve_render_device(render_device); + + let gbm = match GbmDevice::open(&path) { + Ok(g) => g, + Err(e) => { + let _ = + result_tx.blocking_send(Err(format!("failed to open GBM device on {path}: {e}"))); + return; + }, + }; + + let display = match libva::Display::open_drm_display(&path) { + Ok(d) => d, + Err(e) => { + let _ = + result_tx.blocking_send(Err(format!("failed to open VA display on {path}: {e}"))); + return; + }, + }; + tracing::info!(device = %path, "VA-API AV1 decoder opened display"); + + // ── Create stateless decoder ───────────────────────────────────────── + let mut decoder = match StatelessDecoder::>::new_vaapi( + display, + BlockingMode::Blocking, + ) { + Ok(d) => d, + Err(e) => { + let _ = + result_tx.blocking_send(Err(format!("failed to create VA-API AV1 decoder: {e}"))); + return; + }, + }; + + // Stream resolution — updated on FormatChanged events. + let mut coded_width: u32 = 0; + let mut coded_height: u32 = 0; + + while let Some((data, metadata)) = decode_rx.blocking_recv() { + if result_tx.is_closed() { + return; + } + + let decode_start = Instant::now(); + let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(0); + + // Feed bitstream to the decoder. The decoder may process it in + // multiple chunks and may require event handling between calls. + let mut offset = 0usize; + let bitstream = data.as_ref(); + let mut eagain_empty_retries: u32 = 0; + + while offset < bitstream.len() { + let gbm_ref = Arc::clone(&gbm); + let cw = coded_width; + let ch = coded_height; + let mut alloc_cb = move || { + gbm_ref + .clone() + .new_frame( + nv12_fourcc(), + CrosResolution { width: cw, height: ch }, + CrosResolution { width: cw, height: ch }, + GbmUsage::Decode, + ) + .ok() + }; + + let mut made_progress = false; + + match decoder.decode(timestamp, &bitstream[offset..], &mut alloc_cb) { + Ok(bytes_consumed) => { + offset += bytes_consumed; + made_progress = true; + }, + Err(DecodeError::CheckEvents | DecodeError::NotEnoughOutputBuffers(_)) => { + // Process pending events / drain ready frames, then retry. + }, + Err(e) => { + tracing::error!(error = %e, "VA-API AV1 decode error"); + let _ = result_tx.blocking_send(Err(format!("VA-API AV1 decode error: {e}"))); + break; + }, + } + + // Process all pending events (format changes + ready frames). + let (should_exit, had_events) = drain_decoder_events( + &mut decoder, + result_tx, + metadata.as_ref(), + &mut coded_width, + &mut coded_height, + ); + if should_exit { + return; + } + + if made_progress || had_events { + eagain_empty_retries = 0; + } else { + eagain_empty_retries += 1; + if eagain_empty_retries > MAX_EAGAIN_EMPTY_RETRIES { + tracing::error!( + "VA-API AV1 decoder stuck: no progress after {MAX_EAGAIN_EMPTY_RETRIES} retries" + ); + let _ = result_tx.blocking_send(Err( + "VA-API AV1 decoder stuck in CheckEvents/NotEnoughOutputBuffers loop" + .to_string(), + )); + break; + } + // Progressive backoff to avoid a tight spin-loop. + if eagain_empty_retries <= EAGAIN_YIELD_THRESHOLD { + std::thread::yield_now(); + } else { + std::thread::sleep(std::time::Duration::from_millis(1)); + } + } + } + + duration_histogram.record(decode_start.elapsed().as_secs_f64(), &[]); + } + + // Flush remaining frames from the decoder. + if result_tx.is_closed() { + return; + } + if let Err(e) = decoder.flush() { + tracing::warn!(error = %e, "VA-API AV1 decoder flush failed"); + } + drain_decoder_events(&mut decoder, result_tx, None, &mut coded_width, &mut coded_height); +} + +/// Drain all pending events from the decoder. +/// +/// Returns `(should_exit, had_events)`: +/// - `should_exit`: the result channel is closed and the caller should return. +/// - `had_events`: at least one event (format change or frame) was processed. +fn drain_decoder_events( + decoder: &mut StatelessDecoder>, + result_tx: &mpsc::Sender>, + metadata: Option<&PacketMetadata>, + coded_width: &mut u32, + coded_height: &mut u32, +) -> (bool, bool) { + let mut had_events = false; + while let Some(event) = decoder.next_event() { + had_events = true; + match event { + DecoderEvent::FormatChanged => { + if let Some(info) = decoder.stream_info() { + let dw = info.display_resolution.width; + let dh = info.display_resolution.height; + *coded_width = info.coded_resolution.width; + *coded_height = info.coded_resolution.height; + tracing::info!( + display_width = dw, + display_height = dh, + coded_width = *coded_width, + coded_height = *coded_height, + "VA-API AV1 decoder stream format changed" + ); + } + }, + DecoderEvent::FrameReady(handle) => { + if let Err(e) = handle.sync() { + tracing::error!(error = %e, "VA-API AV1 frame sync failed"); + continue; + } + + let display_res = handle.display_resolution(); + let frame_w = display_res.width; + let frame_h = display_res.height; + + let gbm_frame = handle.video_frame(); + let pitches = gbm_frame.get_plane_pitch(); + + // Extract NV12 data while the mapping is alive, then drop the + // mapping before `gbm_frame` to satisfy the borrow checker. + let nv12_data = { + let mapping = match gbm_frame.map() { + Ok(m) => m, + Err(e) => { + tracing::error!(error = %e, "failed to map decoded GBM frame"); + continue; + }, + }; + read_nv12_from_mapping(mapping.as_ref(), frame_w, frame_h, &pitches) + }; + + match VideoFrame::with_metadata( + frame_w, + frame_h, + PixelFormat::Nv12, + nv12_data, + metadata.cloned(), + ) { + Ok(frame) => { + if result_tx.blocking_send(Ok(frame)).is_err() { + return (true, had_events); + } + }, + Err(e) => { + tracing::error!( + error = %e, + "failed to construct VideoFrame from decoded data" + ); + }, + } + }, + } + } + (false, had_events) +} + +// --------------------------------------------------------------------------- +// Encoder +// --------------------------------------------------------------------------- + +/// Configuration for the VA-API AV1 hardware encoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct VaapiAv1EncoderConfig { + /// Path to the DRM render device (e.g. `/dev/dri/renderD128`). + /// When `None`, auto-detects the first VA-API capable device. + pub render_device: Option, + + /// Constant quality parameter (QP). Lower values produce higher quality + /// at the cost of larger bitstream. Range depends on the driver; typical + /// range is 0–255, default 128. + /// + /// Note: VA-API AV1 encoding via cros-codecs currently supports only the + /// `ConstantQuality` rate control mode, not `ConstantBitrate`. + pub quality: u32, + + /// Target framerate in frames per second (used for rate control hints). + pub framerate: u32, + + /// Use low-power encoding mode if the driver supports it. + /// Low-power mode uses the GPU's fixed-function encoder (if available) + /// rather than shader-based encoding, typically offering lower latency + /// at reduced quality flexibility. + pub low_power: bool, + + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, +} + +const fn default_quality() -> u32 { + DEFAULT_QUALITY +} + +const fn default_framerate() -> u32 { + DEFAULT_FRAMERATE +} + +impl Default for VaapiAv1EncoderConfig { + fn default() -> Self { + Self { + render_device: None, + quality: DEFAULT_QUALITY, + framerate: DEFAULT_FRAMERATE, + low_power: false, + hw_accel: HwAccelMode::Auto, + } + } +} + +pub struct VaapiAv1EncoderNode { + config: VaapiAv1EncoderConfig, +} + +impl VaapiAv1EncoderNode { + #[allow(clippy::missing_errors_doc)] + pub fn new(config: VaapiAv1EncoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "VaapiAv1EncoderNode only supports hardware encoding; \ + use video::av1::encoder for CPU encode" + .into(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for VaapiAv1EncoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![ + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::I420, + }), + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + ], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::Av1, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + fn content_type(&self) -> Option { + Some(AV1_CONTENT_TYPE.to_string()) + } + + async fn run(self: Box, context: NodeContext) -> Result<(), StreamKitError> { + encoder_trait::run_encoder(*self, context).await + } +} + +impl EncoderNodeRunner for VaapiAv1EncoderNode { + const CONTENT_TYPE: &'static str = AV1_CONTENT_TYPE; + const NODE_LABEL: &'static str = "VaapiAv1EncoderNode"; + const PACKETS_COUNTER_NAME: &'static str = "vaapi_av1_encoder_packets_processed"; + const DURATION_HISTOGRAM_NAME: &'static str = "vaapi_av1_encode_duration"; + + fn spawn_codec_task( + self, + encode_rx: mpsc::Receiver<(VideoFrame, Option)>, + result_tx: mpsc::Sender>, + duration_histogram: opentelemetry::metrics::Histogram, + ) -> tokio::task::JoinHandle<()> { + encoder_trait::spawn_standard_encode_task::( + self.config, + encode_rx, + result_tx, + duration_histogram, + ) + } +} + +// --------------------------------------------------------------------------- +// Encoder — internal codec wrapper +// --------------------------------------------------------------------------- + +/// Type alias for the full VA-API AV1 encoder with GBM-backed frames. +type CrosVaapiAv1Encoder = StatelessEncoder< + cros_codecs::encoder::av1::AV1, + GbmVideoFrame, + cros_codecs::backend::vaapi::encoder::VaapiBackend< + GbmExternalBufferDescriptor, + libva::Surface, + >, +>; + +/// Internal encoder state wrapping the cros-codecs `StatelessEncoder`. +/// +/// `!Send` due to internal `Rc` — lives entirely inside +/// a `spawn_blocking` thread, matching the pattern in `av1.rs`. +struct VaapiAv1Encoder { + encoder: CrosVaapiAv1Encoder, + gbm: Arc, + width: u32, + height: u32, + coded_width: u32, + coded_height: u32, + frame_count: u64, +} + +impl StandardVideoEncoder for VaapiAv1Encoder { + type Config = VaapiAv1EncoderConfig; + const CODEC_NAME: &'static str = "VA-API AV1"; + + fn new_encoder(width: u32, height: u32, config: &Self::Config) -> Result { + let (display, gbm, path) = open_va_and_gbm(config.render_device.as_ref())?; + tracing::info!(device = %path, width, height, "VA-API AV1 encoder opening"); + + let coded_width = align_up_u32(width, AV1_SB_SIZE); + let coded_height = align_up_u32(height, AV1_SB_SIZE); + + let cros_config = CrosEncoderConfig { + profile: Av1Profile::Profile0, + bit_depth: cros_codecs::codec::av1::parser::BitDepth::Depth8, + resolution: CrosResolution { width: coded_width, height: coded_height }, + pred_structure: PredictionStructure::LowDelay { limit: 1024 }, + initial_tunings: Tunings { + rate_control: RateControl::ConstantQuality(config.quality), + framerate: config.framerate, + min_quality: 0, + max_quality: 255, + }, + }; + + let encoder = CrosVaapiAv1Encoder::new_vaapi( + display, + cros_config, + nv12_fourcc(), + CrosResolution { width: coded_width, height: coded_height }, + config.low_power, + BlockingMode::Blocking, + ) + .map_err(|e| format!("failed to create VA-API AV1 encoder: {e}"))?; + + tracing::info!( + device = %path, + width, + height, + coded_width, + coded_height, + quality = config.quality, + "VA-API AV1 encoder created" + ); + + Ok(Self { encoder, gbm, width, height, coded_width, coded_height, frame_count: 0 }) + } + + fn encode( + &mut self, + frame: &VideoFrame, + metadata: Option, + ) -> Result, String> { + if frame.pixel_format == PixelFormat::Rgba8 { + return Err("VA-API AV1 encoder requires NV12 or I420 input; \ + insert a video::pixel_convert node upstream" + .into()); + } + + // Create a GBM frame and upload the raw video data. + let mut gbm_frame = Arc::clone(&self.gbm) + .new_frame( + nv12_fourcc(), + CrosResolution { width: self.width, height: self.height }, + CrosResolution { width: self.coded_width, height: self.coded_height }, + GbmUsage::Encode, + ) + .map_err(|e| format!("failed to allocate GBM frame for encoding: {e}"))?; + + // Write frame data into the GBM buffer. + let pitches = gbm_frame.get_plane_pitch(); + { + let mapping = gbm_frame + .map_mut() + .map_err(|e| format!("failed to map GBM frame for writing: {e}"))?; + write_nv12_to_mapping(mapping.as_ref(), frame, &pitches)?; + } + + let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); + let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); + + let frame_layout = FrameLayout { + format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR + size: CrosResolution { width: self.coded_width, height: self.coded_height }, + planes: vec![ + PlaneLayout { + buffer_index: 0, + offset: 0, + stride: pitches.first().copied().unwrap_or(self.width as usize), + }, + PlaneLayout { + buffer_index: 0, + offset: pitches.first().copied().unwrap_or(self.width as usize) + * self.coded_height as usize, + stride: pitches.get(1).copied().unwrap_or(self.width as usize), + }, + ], + }; + + let cros_meta = + CrosFrameMetadata { timestamp, layout: frame_layout, force_keyframe: is_keyframe }; + + self.encoder + .encode(cros_meta, gbm_frame) + .map_err(|e| format!("VA-API AV1 encode error: {e}"))?; + + self.frame_count += 1; + + // Poll for all available encoded output. + let mut packets = Vec::new(); + loop { + match self.encoder.poll() { + Ok(Some(coded)) => { + packets.push(EncodedPacket { + data: Bytes::from(coded.bitstream), + metadata: metadata.clone(), + }); + }, + Ok(None) => break, + Err(e) => return Err(format!("VA-API AV1 encoder poll error: {e}")), + } + } + + Ok(packets) + } + + fn flush_encoder(&mut self) -> Result, String> { + self.encoder.drain().map_err(|e| format!("VA-API AV1 encoder drain error: {e}"))?; + + let mut packets = Vec::new(); + loop { + match self.encoder.poll() { + Ok(Some(coded)) => { + packets + .push(EncodedPacket { data: Bytes::from(coded.bitstream), metadata: None }); + }, + Ok(None) => break, + Err(e) => return Err(format!("VA-API AV1 encoder poll error: {e}")), + } + } + + Ok(packets) + } + + fn flush_on_dimension_change() -> bool { + true + } +} + +// --------------------------------------------------------------------------- +// Registration +// --------------------------------------------------------------------------- + +use schemars::schema_for; +use streamkit_core::registry::StaticPins; + +#[allow(clippy::expect_used, clippy::missing_panics_doc)] +pub fn register_vaapi_av1_nodes(registry: &mut NodeRegistry) { + let default_decoder = VaapiAv1DecoderNode::new(VaapiAv1DecoderConfig::default()) + .expect("default VA-API AV1 decoder config should be valid"); + registry.register_static_with_description( + "video::vaapi::av1_decoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(VaapiAv1DecoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(VaapiAv1DecoderConfig)) + .expect("VaapiAv1DecoderConfig schema should serialize to JSON"), + StaticPins { inputs: default_decoder.input_pins(), outputs: default_decoder.output_pins() }, + vec![ + "video".to_string(), + "codecs".to_string(), + "av1".to_string(), + "hw".to_string(), + "vaapi".to_string(), + ], + false, + "Decodes AV1-compressed packets into raw NV12 video frames using VA-API \ + hardware acceleration. Requires a VA-API capable GPU (Intel Arc+, AMD, \ + or NVIDIA with nvidia-vaapi-driver).", + ); + + let default_encoder = VaapiAv1EncoderNode::new(VaapiAv1EncoderConfig::default()) + .expect("default VA-API AV1 encoder config should be valid"); + registry.register_static_with_description( + "video::vaapi::av1_encoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(VaapiAv1EncoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(VaapiAv1EncoderConfig)) + .expect("VaapiAv1EncoderConfig schema should serialize to JSON"), + StaticPins { inputs: default_encoder.input_pins(), outputs: default_encoder.output_pins() }, + vec![ + "video".to_string(), + "codecs".to_string(), + "av1".to_string(), + "hw".to_string(), + "vaapi".to_string(), + ], + false, + "Encodes raw NV12/I420 video frames into AV1-compressed packets using VA-API \ + hardware acceleration. Uses constant-quality (CQP) rate control. Requires a \ + VA-API capable GPU with AV1 encode support (Intel Arc+, AMD).", + ); +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::disallowed_macros)] +mod tests { + use super::*; + use std::cell::RefCell; + + // ----------------------------------------------------------------------- + // Mock mapping types for unit-testing read/write helpers without a GPU. + // ----------------------------------------------------------------------- + + struct MockReadMapping<'a> { + planes: Vec<&'a [u8]>, + } + + impl<'a> ReadMapping<'a> for MockReadMapping<'a> { + fn get(&self) -> Vec<&[u8]> { + self.planes.clone() + } + } + + struct MockWriteMapping<'a> { + planes: Vec>, + } + + impl<'a> WriteMapping<'a> for MockWriteMapping<'a> { + fn get(&self) -> Vec> { + // Re-borrow each plane to return fresh RefCells. + // SAFETY: this is only used in single-threaded tests where + // the returned RefCells do not outlive `self`. + self.planes + .iter() + .map(|cell| { + let ptr = cell.borrow_mut().as_mut_ptr(); + let len = cell.borrow().len(); + RefCell::new(unsafe { std::slice::from_raw_parts_mut(ptr, len) }) + }) + .collect() + } + } + + // ----------------------------------------------------------------------- + // align_up_u32 + // ----------------------------------------------------------------------- + + #[test] + fn test_align_up_u32_already_aligned() { + assert_eq!(align_up_u32(64, 64), 64); + assert_eq!(align_up_u32(128, 64), 128); + } + + #[test] + fn test_align_up_u32_needs_alignment() { + assert_eq!(align_up_u32(65, 64), 128); + assert_eq!(align_up_u32(1, 64), 64); + assert_eq!(align_up_u32(100, 64), 128); + } + + #[test] + fn test_align_up_u32_alignment_one() { + assert_eq!(align_up_u32(42, 1), 42); + } + + // ----------------------------------------------------------------------- + // read_nv12_from_mapping — buffer size and content + // ----------------------------------------------------------------------- + + #[test] + fn test_read_nv12_even_dimensions() { + let w: u32 = 64; + let h: u32 = 48; + let y_size = (w * h) as usize; + let uv_h = h as usize / 2; + let chroma_w = w as usize; // even width: chroma_w == w + let uv_size = chroma_w * uv_h; + + let y_plane = vec![0xAA_u8; y_size]; + let uv_plane = vec![0x80_u8; uv_size]; + let mapping = MockReadMapping { planes: vec![&y_plane, &uv_plane] }; + let pitches = [w as usize, chroma_w]; + + let data = read_nv12_from_mapping(&mapping, w, h, &pitches); + + let layout = streamkit_core::types::VideoLayout::packed(w, h, PixelFormat::Nv12); + assert_eq!( + data.len(), + layout.total_bytes(), + "output buffer size must match VideoLayout::packed" + ); + assert!(data[..y_size].iter().all(|&b| b == 0xAA), "Y plane data mismatch"); + assert!(data[y_size..].iter().all(|&b| b == 0x80), "UV plane data mismatch"); + } + + #[test] + fn test_read_nv12_odd_width() { + // Odd width exercises the chroma_w = (w+1)/2*2 formula. + let w: u32 = 641; + let h: u32 = 480; + let y_size = (w * h) as usize; + let chroma_w = (w as usize + 1) / 2 * 2; // 642 + let uv_h = h as usize / 2; + let uv_size = chroma_w * uv_h; + + let y_plane = vec![0x10_u8; y_size]; + let uv_plane = vec![0x80_u8; uv_size]; + let mapping = MockReadMapping { planes: vec![&y_plane, &uv_plane] }; + let pitches = [w as usize, chroma_w]; + + let data = read_nv12_from_mapping(&mapping, w, h, &pitches); + + let layout = streamkit_core::types::VideoLayout::packed(w, h, PixelFormat::Nv12); + assert_eq!( + data.len(), + layout.total_bytes(), + "odd-width output buffer must match VideoLayout::packed (chroma_w={chroma_w})" + ); + } + + #[test] + fn test_read_nv12_odd_height() { + let w: u32 = 64; + let h: u32 = 49; // odd height + let y_size = (w * h) as usize; + let chroma_w = w as usize; + let uv_h = (h as usize + 1) / 2; // 25 + let uv_size = chroma_w * uv_h; + + let y_plane = vec![0x10_u8; y_size]; + let uv_plane = vec![0x80_u8; uv_size]; + let mapping = MockReadMapping { planes: vec![&y_plane, &uv_plane] }; + let pitches = [w as usize, chroma_w]; + + let data = read_nv12_from_mapping(&mapping, w, h, &pitches); + + let layout = streamkit_core::types::VideoLayout::packed(w, h, PixelFormat::Nv12); + assert_eq!( + data.len(), + layout.total_bytes(), + "odd-height output buffer must match VideoLayout::packed" + ); + } + + #[test] + fn test_read_nv12_with_stride() { + // Simulate a GBM surface with stride > width (e.g. 128-byte aligned). + let w: u32 = 100; + let h: u32 = 4; + let y_stride = 128_usize; // padded stride + let uv_stride = 128_usize; + let uv_h = 2_usize; + let chroma_w = (w as usize + 1) / 2 * 2; // 100 + + // Build Y plane with stride padding. + let mut y_plane = vec![0u8; y_stride * h as usize]; + for row in 0..h as usize { + for col in 0..w as usize { + y_plane[row * y_stride + col] = 0xAA; + } + } + + // Build UV plane with stride padding. + let mut uv_plane = vec![0u8; uv_stride * uv_h]; + for row in 0..uv_h { + for col in 0..chroma_w { + uv_plane[row * uv_stride + col] = 0x80; + } + } + + let mapping = MockReadMapping { planes: vec![&y_plane, &uv_plane] }; + let pitches = [y_stride, uv_stride]; + + let data = read_nv12_from_mapping(&mapping, w, h, &pitches); + + let layout = streamkit_core::types::VideoLayout::packed(w, h, PixelFormat::Nv12); + assert_eq!(data.len(), layout.total_bytes()); + + // Verify Y data is correctly de-strided. + let y_size = w as usize * h as usize; + assert!(data[..y_size].iter().all(|&b| b == 0xAA)); + // Verify UV data is correctly de-strided. + assert!(data[y_size..].iter().all(|&b| b == 0x80)); + } + + // ----------------------------------------------------------------------- + // read → VideoFrame::with_metadata roundtrip + // ----------------------------------------------------------------------- + + #[test] + fn test_read_nv12_produces_valid_video_frame() { + // The key invariant: read_nv12_from_mapping output must be accepted by + // VideoFrame::with_metadata, which validates against VideoLayout::packed. + for &(w, h) in &[(64, 48), (641, 480), (1920, 1080), (1921, 1081)] { + let y_size = (w * h) as usize; + let chroma_w = (w as usize + 1) / 2 * 2; + let uv_h = (h as usize + 1) / 2; + let uv_size = chroma_w * uv_h; + + let y_plane = vec![0x10_u8; y_size]; + let uv_plane = vec![0x80_u8; uv_size]; + let mapping = MockReadMapping { planes: vec![&y_plane, &uv_plane] }; + let pitches = [w as usize, chroma_w]; + + let data = read_nv12_from_mapping(&mapping, w, h, &pitches); + let result = VideoFrame::with_metadata(w, h, PixelFormat::Nv12, data, None); + assert!( + result.is_ok(), + "VideoFrame::with_metadata failed for {w}x{h}: {:?}", + result.err() + ); + } + } + + // ----------------------------------------------------------------------- + // write_nv12_to_mapping — NV12 source + // ----------------------------------------------------------------------- + + #[test] + fn test_write_nv12_even_dimensions() { + let w: u32 = 64; + let h: u32 = 48; + let frame = crate::test_utils::create_test_video_frame(w, h, PixelFormat::Nv12, 0xAA); + + let y_size = (w * h) as usize; + let chroma_w = (w as usize + 1) / 2 * 2; + let uv_h = (h as usize + 1) / 2; + + let mut y_buf = vec![0u8; y_size]; + let mut uv_buf = vec![0u8; chroma_w * uv_h]; + + let mapping = + MockWriteMapping { planes: vec![RefCell::new(&mut y_buf), RefCell::new(&mut uv_buf)] }; + let pitches = [w as usize, chroma_w]; + + let result = write_nv12_to_mapping(&mapping, &frame, &pitches); + assert!(result.is_ok(), "write_nv12_to_mapping failed: {:?}", result.err()); + + // Y plane should be filled with 0xAA. + assert!(y_buf.iter().all(|&b| b == 0xAA), "Y plane should contain frame data"); + } + + #[test] + fn test_write_nv12_odd_width() { + let w: u32 = 641; + let h: u32 = 480; + let frame = crate::test_utils::create_test_video_frame(w, h, PixelFormat::Nv12, 0x10); + + let y_size = (w * h) as usize; + let chroma_w = (w as usize + 1) / 2 * 2; // 642 + let uv_h = (h as usize + 1) / 2; + + let mut y_buf = vec![0u8; y_size]; + let mut uv_buf = vec![0u8; chroma_w * uv_h]; + + let mapping = + MockWriteMapping { planes: vec![RefCell::new(&mut y_buf), RefCell::new(&mut uv_buf)] }; + let pitches = [w as usize, chroma_w]; + + let result = write_nv12_to_mapping(&mapping, &frame, &pitches); + assert!( + result.is_ok(), + "write_nv12_to_mapping should handle odd width {w}: {:?}", + result.err() + ); + } + + // ----------------------------------------------------------------------- + // write_nv12_to_mapping — I420 → NV12 conversion + // ----------------------------------------------------------------------- + + #[test] + fn test_write_i420_to_nv12_conversion() { + let w: u32 = 64; + let h: u32 = 48; + let frame = crate::test_utils::create_test_video_frame(w, h, PixelFormat::I420, 0x10); + + let y_size = (w * h) as usize; + let chroma_w = (w as usize + 1) / 2 * 2; + let uv_h = (h as usize + 1) / 2; + + let mut y_buf = vec![0u8; y_size]; + let mut uv_buf = vec![0u8; chroma_w * uv_h]; + + let mapping = + MockWriteMapping { planes: vec![RefCell::new(&mut y_buf), RefCell::new(&mut uv_buf)] }; + let pitches = [w as usize, chroma_w]; + + let result = write_nv12_to_mapping(&mapping, &frame, &pitches); + assert!(result.is_ok(), "I420→NV12 conversion failed: {:?}", result.err()); + + // Y plane should have the fill value. + assert!(y_buf.iter().all(|&b| b == 0x10), "Y plane should contain I420 luma data"); + + // UV plane should have interleaved U/V values (128 for neutral chroma + // from create_test_video_frame). + let uv_w = w.div_ceil(2) as usize; + for row in 0..uv_h { + for col in 0..uv_w { + let idx = row * chroma_w + col * 2; + assert_eq!(uv_buf[idx], 128, "U value at row={row} col={col}"); + assert_eq!(uv_buf[idx + 1], 128, "V value at row={row} col={col}"); + } + } + } + + #[test] + fn test_write_i420_to_nv12_odd_width() { + // Odd width exercises the UV stride fallback path — the fix ensures + // the fallback uses `uv_w * 2` instead of `w` so rows don't misalign. + let w: u32 = 641; + let h: u32 = 480; + let frame = crate::test_utils::create_test_video_frame(w, h, PixelFormat::I420, 0x10); + + let y_size = (w * h) as usize; + let uv_w = w.div_ceil(2) as usize; // 321 + let chroma_w = uv_w * 2; // 642 + let uv_h = (h as usize + 1) / 2; + + let mut y_buf = vec![0u8; y_size]; + let mut uv_buf = vec![0u8; chroma_w * uv_h]; + + let mapping = + MockWriteMapping { planes: vec![RefCell::new(&mut y_buf), RefCell::new(&mut uv_buf)] }; + // Deliberately omit pitches to exercise the fallback. + let pitches: [usize; 0] = []; + + let result = write_nv12_to_mapping(&mapping, &frame, &pitches); + assert!(result.is_ok(), "I420→NV12 odd-width conversion failed: {:?}", result.err()); + + // Verify UV interleaving on the last row to catch misalignment. + let last_row = uv_h - 1; + for col in 0..uv_w { + let idx = last_row * chroma_w + col * 2; + assert_eq!(uv_buf[idx], 128, "U at last row col={col}"); + assert_eq!(uv_buf[idx + 1], 128, "V at last row col={col}"); + } + } + + // ----------------------------------------------------------------------- + // write_nv12_to_mapping — unsupported pixel format + // ----------------------------------------------------------------------- + + #[test] + fn test_write_unsupported_format_returns_error() { + let w: u32 = 64; + let h: u32 = 48; + let frame = crate::test_utils::create_test_video_frame(w, h, PixelFormat::Rgba8, 0xFF); + + let mut y_buf = vec![0u8; (w * h) as usize]; + let mut uv_buf = vec![0u8; (w as usize) * (h as usize / 2)]; + + let mapping = + MockWriteMapping { planes: vec![RefCell::new(&mut y_buf), RefCell::new(&mut uv_buf)] }; + let pitches = [w as usize, w as usize]; + + let result = write_nv12_to_mapping(&mapping, &frame, &pitches); + assert!(result.is_err(), "RGBA8 input should be rejected"); + assert!( + result.unwrap_err().contains("requires NV12 or I420"), + "error message should mention supported formats" + ); + } + + // ----------------------------------------------------------------------- + // NV12 read→write roundtrip + // ----------------------------------------------------------------------- + + #[test] + fn test_nv12_read_write_roundtrip() { + // Verify that data read from a mapping can be written back and + // produces identical plane content. + for &(w, h) in &[(64, 48), (640, 480), (641, 481)] { + let y_size = (w * h) as usize; + let chroma_w = (w as usize + 1) / 2 * 2; + let uv_h = (h as usize + 1) / 2; + let uv_size = chroma_w * uv_h; + + // Create source planes with deterministic data. + let y_src: Vec = (0..y_size).map(|i| (i % 256) as u8).collect(); + let uv_src: Vec = (0..uv_size).map(|i| ((i + 128) % 256) as u8).collect(); + + // Read from mapping. + let read_mapping = MockReadMapping { planes: vec![&y_src, &uv_src] }; + let pitches = [w as usize, chroma_w]; + let data = read_nv12_from_mapping(&read_mapping, w, h, &pitches); + + // Create a VideoFrame from the read data. + let frame = VideoFrame::with_metadata(w, h, PixelFormat::Nv12, data, None).unwrap(); + + // Write back to a new mapping. + let mut y_dst = vec![0u8; y_size]; + let mut uv_dst = vec![0u8; uv_size]; + let write_mapping = MockWriteMapping { + planes: vec![RefCell::new(&mut y_dst), RefCell::new(&mut uv_dst)], + }; + write_nv12_to_mapping(&write_mapping, &frame, &pitches).unwrap(); + + assert_eq!(y_dst, y_src, "Y plane roundtrip failed for {w}x{h}"); + assert_eq!(uv_dst, uv_src, "UV plane roundtrip failed for {w}x{h}"); + } + } + + // ----------------------------------------------------------------------- + // resolve_render_device + // ----------------------------------------------------------------------- + + #[test] + fn test_resolve_render_device_with_configured() { + let configured = "/dev/dri/renderD129".to_string(); + let result = resolve_render_device(Some(&configured)); + assert_eq!(result, "/dev/dri/renderD129"); + } + + #[test] + fn test_resolve_render_device_fallback() { + // Without a configured device and without real hardware, falls back + // to default or auto-detected device. + let result = resolve_render_device(None); + assert!(!result.is_empty(), "should return a non-empty device path"); + } + + // ----------------------------------------------------------------------- + // GPU integration tests — encode/decode roundtrip + // + // These require a VA-API capable GPU. They are compiled with the `vaapi` + // feature but skip at runtime if no VA-API device is available. + // ----------------------------------------------------------------------- + + /// Check whether a usable VA-API display can be opened. + fn vaapi_available() -> bool { + let path = resolve_render_device(None); + libva::Display::open_drm_display(std::path::Path::new(&path)).is_ok() + } + + /// Encoder + Decoder roundtrip: encode 5 NV12 frames, decode them back, + /// verify dimensions and pixel format. + #[tokio::test] + async fn test_vaapi_av1_encode_decode_roundtrip() { + if !vaapi_available() { + eprintln!("SKIP: no VA-API device available"); + return; + } + + use crate::test_utils::{ + assert_state_initializing, assert_state_running, assert_state_stopped, + create_test_context, create_test_video_frame, + }; + use std::borrow::Cow; + use std::collections::HashMap; + + // --- Encode --- + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder_config = VaapiAv1EncoderConfig { + render_device: None, + hw_accel: HwAccelMode::Auto, + quality: 200, // fast, lower quality for test speed + framerate: 30, + low_power: false, + }; + let encoder = VaapiAv1EncoderNode::new(encoder_config).unwrap(); + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + for index in 0_u64..5 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(1_000 + 33_333 * index), + duration_us: Some(33_333), + sequence: Some(index), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty(), "VA-API AV1 encoder produced no packets"); + + // --- Decode --- + let (dec_input_tx, dec_input_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_input_rx); + + let (dec_context, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = VaapiAv1DecoderNode::new(VaapiAv1DecoderConfig::default()).unwrap(); + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_context).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + for packet in encoded_packets { + if let Packet::Binary { data, metadata, .. } = packet { + dec_input_tx + .send(Packet::Binary { + data, + content_type: Some(Cow::Borrowed(AV1_CONTENT_TYPE)), + metadata, + }) + .await + .unwrap(); + } + } + drop(dec_input_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "VA-API AV1 decoder produced no frames"); + + for packet in decoded_packets { + match packet { + Packet::Video(frame) => { + assert_eq!(frame.width, 64); + assert_eq!(frame.height, 64); + assert_eq!(frame.pixel_format, PixelFormat::Nv12); + assert!(!frame.data().is_empty(), "Decoded frame should have data"); + }, + _ => panic!("Expected Video packet from VA-API AV1 decoder"), + } + } + } + + /// Verify decoded frames preserve metadata from input packets. + #[tokio::test] + async fn test_vaapi_av1_metadata_propagation() { + if !vaapi_available() { + eprintln!("SKIP: no VA-API device available"); + return; + } + + use crate::test_utils::{ + assert_state_initializing, assert_state_running, assert_state_stopped, + create_test_context, create_test_video_frame, + }; + use std::borrow::Cow; + use std::collections::HashMap; + + // --- Encode --- + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder = VaapiAv1EncoderNode::new(VaapiAv1EncoderConfig { + render_device: None, + hw_accel: HwAccelMode::Auto, + quality: 200, + framerate: 30, + low_power: false, + }) + .unwrap(); + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + let timestamps: Vec = vec![1_000, 34_333, 67_666]; + for (i, &ts) in timestamps.iter().enumerate() { + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(ts), + duration_us: Some(33_333), + sequence: Some(i as u64), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty()); + + // --- Decode and verify metadata --- + let (dec_input_tx, dec_input_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_input_rx); + + let (dec_context, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = VaapiAv1DecoderNode::new(VaapiAv1DecoderConfig::default()).unwrap(); + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_context).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + for packet in encoded_packets { + if let Packet::Binary { data, metadata, .. } = packet { + dec_input_tx + .send(Packet::Binary { + data, + content_type: Some(Cow::Borrowed(AV1_CONTENT_TYPE)), + metadata, + }) + .await + .unwrap(); + } + } + drop(dec_input_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "Decoder should produce at least one frame"); + + for (i, packet) in decoded_packets.iter().enumerate() { + match packet { + Packet::Video(frame) => { + assert!(frame.metadata.is_some(), "Decoded frame {i} should have metadata"); + }, + _ => panic!("Expected Video packet from VA-API AV1 decoder"), + } + } + } + + /// Encode I420 input frames and verify the encoder accepts them + /// (exercises the I420→NV12 conversion path). + #[tokio::test] + async fn test_vaapi_av1_encode_i420_input() { + if !vaapi_available() { + eprintln!("SKIP: no VA-API device available"); + return; + } + + use crate::test_utils::{ + assert_state_initializing, assert_state_running, assert_state_stopped, + create_test_context, create_test_video_frame, + }; + use std::collections::HashMap; + + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder = VaapiAv1EncoderNode::new(VaapiAv1EncoderConfig { + render_device: None, + hw_accel: HwAccelMode::Auto, + quality: 200, + framerate: 30, + low_power: false, + }) + .unwrap(); + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + for index in 0_u64..3 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::I420, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(33_333 * index), + duration_us: Some(33_333), + sequence: Some(index), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!( + !encoded_packets.is_empty(), + "VA-API AV1 encoder should accept I420 input and produce packets" + ); + } + + /// Verify ForceCpu mode returns an error (VA-API is HW-only). + #[test] + fn test_vaapi_force_cpu_returns_error() { + let decoder_config = + VaapiAv1DecoderConfig { render_device: None, hw_accel: HwAccelMode::ForceCpu }; + let result = VaapiAv1DecoderNode::new(decoder_config); + assert!(result.is_err(), "ForceCpu should be rejected for VA-API decoder"); + + let encoder_config = VaapiAv1EncoderConfig { + render_device: None, + hw_accel: HwAccelMode::ForceCpu, + quality: DEFAULT_QUALITY, + framerate: DEFAULT_FRAMERATE, + low_power: false, + }; + let result = VaapiAv1EncoderNode::new(encoder_config); + assert!(result.is_err(), "ForceCpu should be rejected for VA-API encoder"); + } + + // ----------------------------------------------------------------------- + // deny_unknown_fields + // ----------------------------------------------------------------------- + + #[test] + fn test_deny_unknown_fields_decoder() { + let json = r#"{"render_device":null,"hw_accel":"auto","bogus":1}"#; + let result: Result = serde_json::from_str(json); + assert!(result.is_err(), "Unknown fields should be rejected"); + } + + #[test] + fn test_deny_unknown_fields_encoder() { + let json = r#"{"quality":128,"unknown_key":"oops"}"#; + let result: Result = serde_json::from_str(json); + assert!(result.is_err(), "Unknown fields should be rejected"); + } +} diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs new file mode 100644 index 00000000..e0321da6 --- /dev/null +++ b/crates/nodes/src/video/vulkan_video.rs @@ -0,0 +1,1461 @@ +// SPDX-FileCopyrightText: © 2025 StreamKit Contributors +// +// SPDX-License-Identifier: MPL-2.0 + +//! Vulkan Video HW-accelerated H.264 encoder and decoder nodes. +//! +//! Uses the [`vk-video`](https://crates.io/crates/vk-video) crate which wraps +//! the Vulkan Video extensions and integrates natively with `wgpu`. Decoded +//! frames are `wgpu::Texture`s — enabling a zero-copy path with the GPU +//! compositor in the future. +//! +//! This module provides: +//! - `VulkanVideoH264DecoderNode` — decodes H.264 packets to NV12 `VideoFrame`s +//! - `VulkanVideoH264EncoderNode` — encodes NV12 `VideoFrame`s to H.264 packets +//! +//! Both nodes perform runtime capability detection: if no Vulkan Video capable +//! GPU is found, node creation returns an error so the pipeline can fall back +//! to a CPU codec. +//! +//! # Feature gate +//! +//! Requires `vulkan_video` feature. + +use std::borrow::Cow; +use std::num::NonZeroU32; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use async_trait::async_trait; +use bytes::Bytes; +use opentelemetry::global; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use streamkit_core::stats::NodeStatsTracker; +use streamkit_core::types::{ + EncodedVideoFormat, Packet, PacketMetadata, PacketType, PixelFormat, RawVideoFormat, + VideoCodec, VideoFrame, VideoLayout, +}; +use streamkit_core::{ + config_helpers, get_codec_channel_capacity, packet_helpers, state_helpers, InputPin, + NodeContext, NodeRegistry, OutputPin, PinCardinality, PooledVideoData, ProcessorNode, + StreamKitError, VideoFramePool, +}; +use tokio::sync::mpsc; + +use super::HwAccelMode; +use super::H264_CONTENT_TYPE; + +// --------------------------------------------------------------------------- +// Decoder +// --------------------------------------------------------------------------- + +/// Configuration for the Vulkan Video H.264 decoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct VulkanVideoH264DecoderConfig { + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, +} + +impl Default for VulkanVideoH264DecoderConfig { + fn default() -> Self { + Self { hw_accel: HwAccelMode::Auto } + } +} + +/// Vulkan Video H.264 decoder node. +/// +/// Accepts H.264 encoded `Binary` packets on its `"in"` pin and emits +/// decoded NV12 `VideoFrame`s on its `"out"` pin. +/// +/// Internally uses `vk-video::BytesDecoder` for GPU-accelerated decoding, +/// which returns raw NV12 pixel data directly — avoiding explicit GPU +/// texture readback while still leveraging the Vulkan Video decode engine. +pub struct VulkanVideoH264DecoderNode { + config: VulkanVideoH264DecoderConfig, +} + +impl VulkanVideoH264DecoderNode { + /// Create a new decoder node with the given configuration. + /// + /// # Errors + /// + /// Returns an error if `hw_accel` is `ForceCpu` — this node only + /// supports hardware decoding. Capability probing is deferred to + /// `run()`. + pub fn new(config: VulkanVideoH264DecoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "VulkanVideoH264DecoderNode only supports hardware decoding; \ + use an OpenH264 decoder for CPU-only mode" + .to_string(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for VulkanVideoH264DecoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::H264, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + })], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + async fn run(self: Box, mut context: NodeContext) -> Result<(), StreamKitError> { + let node_name = context.output_sender.node_name().to_string(); + state_helpers::emit_initializing(&context.state_tx, &node_name); + + tracing::info!("VulkanVideoH264DecoderNode starting (hw_accel={:?})", self.config.hw_accel); + let mut input_rx = context.take_input("in")?; + let video_pool = context.video_pool.clone(); + + // ── Metrics ────────────────────────────────────────────────────── + let meter = global::meter("skit_nodes"); + let packets_processed_counter = + meter.u64_counter("vulkan_video_h264_decoder_packets_processed").build(); + let decode_duration_histogram = meter + .f64_histogram("vulkan_video_h264_decode_duration") + .with_boundaries(streamkit_core::metrics::HISTOGRAM_BOUNDARIES_CODEC_PACKET.to_vec()) + .build(); + + // ── Channels ───────────────────────────────────────────────────── + let (decode_tx, mut decode_rx) = + mpsc::channel::<(Bytes, Option)>(get_codec_channel_capacity()); + let (result_tx, mut result_rx) = + mpsc::channel::>(get_codec_channel_capacity()); + + // ── Blocking decode task ───────────────────────────────────────── + let decode_task = tokio::task::spawn_blocking(move || { + let instance = match vk_video::VulkanInstance::new() { + Ok(inst) => inst, + Err(err) => { + let _ = result_tx + .blocking_send(Err(format!("failed to create VulkanInstance: {err}"))); + return; + }, + }; + + let adapter = match instance + .create_adapter(&vk_video::parameters::VulkanAdapterDescriptor::default()) + { + Ok(a) => a, + Err(err) => { + let _ = result_tx + .blocking_send(Err(format!("failed to create VulkanAdapter: {err}"))); + return; + }, + }; + + let device = match adapter + .create_device(&vk_video::parameters::VulkanDeviceDescriptor::default()) + { + Ok(d) => d, + Err(err) => { + let _ = result_tx + .blocking_send(Err(format!("failed to create VulkanDevice: {err}"))); + return; + }, + }; + + if !device.supports_decoding() { + let _ = result_tx.blocking_send(Err( + "Vulkan device does not support video decoding".to_string(), + )); + return; + } + + let mut decoder = match device + .create_bytes_decoder(vk_video::parameters::DecoderParameters::default()) + { + Ok(dec) => dec, + Err(err) => { + let _ = result_tx + .blocking_send(Err(format!("failed to create BytesDecoder: {err}"))); + return; + }, + }; + + tracing::info!("Vulkan Video H.264 decoder initialised successfully"); + + while let Some((data, metadata)) = decode_rx.blocking_recv() { + if result_tx.is_closed() { + return; + } + + let pts = metadata.as_ref().and_then(|m| m.timestamp_us); + + let decode_start = Instant::now(); + let decode_result = + decoder.decode(vk_video::EncodedInputChunk { data: &data, pts }); + decode_duration_histogram.record(decode_start.elapsed().as_secs_f64(), &[]); + + match decode_result { + Ok(frames) => { + for output_frame in frames { + match raw_frame_to_video_frame( + &output_frame, + metadata.clone(), + video_pool.as_ref(), + ) { + Ok(vf) => { + if result_tx.blocking_send(Ok(vf)).is_err() { + return; + } + }, + Err(err) => { + let _ = result_tx.blocking_send(Err(err)); + }, + } + } + }, + Err(err) => { + let _ = result_tx + .blocking_send(Err(format!("Vulkan Video H.264 decode error: {err}"))); + }, + } + } + + // Flush remaining buffered frames. + if result_tx.is_closed() { + return; + } + match decoder.flush() { + Ok(frames) => { + for output_frame in frames { + match raw_frame_to_video_frame(&output_frame, None, video_pool.as_ref()) { + Ok(vf) => { + if result_tx.blocking_send(Ok(vf)).is_err() { + return; + } + }, + Err(err) => { + let _ = result_tx.blocking_send(Err(err)); + }, + } + } + }, + Err(err) => { + let _ = result_tx + .blocking_send(Err(format!("Vulkan Video H.264 flush error: {err}"))); + }, + } + }); + + // ── State transition ───────────────────────────────────────────── + state_helpers::emit_running(&context.state_tx, &node_name); + let mut stats_tracker = NodeStatsTracker::new(node_name.clone(), context.stats_tx.clone()); + let batch_size = context.batch_size; + + // ── Input task ─────────────────────────────────────────────────── + let decode_tx_clone = decode_tx.clone(); + let mut input_task = tokio::spawn(async move { + loop { + let Some(first_packet) = input_rx.recv().await else { + break; + }; + + let packet_batch = + packet_helpers::batch_packets_greedy(first_packet, &mut input_rx, batch_size); + + for packet in packet_batch { + if let Packet::Binary { data, metadata, .. } = packet { + if decode_tx_clone.send((data, metadata)).await.is_err() { + tracing::error!( + "VulkanVideoH264DecoderNode decode task has shut down unexpectedly" + ); + return; + } + } + } + } + tracing::info!("VulkanVideoH264DecoderNode input stream closed"); + }); + + // ── Forward loop ───────────────────────────────────────────────── + crate::codec_utils::codec_forward_loop( + &mut context, + &mut result_rx, + &mut input_task, + decode_task, + decode_tx, + &packets_processed_counter, + &mut stats_tracker, + Packet::Video, + "VulkanVideoH264DecoderNode", + ) + .await; + + state_helpers::emit_stopped(&context.state_tx, &node_name, "input_closed"); + tracing::info!("VulkanVideoH264DecoderNode finished"); + Ok(()) + } +} + +/// Convert a vk-video `OutputFrame` into a StreamKit `VideoFrame`. +fn raw_frame_to_video_frame( + output_frame: &vk_video::OutputFrame, + metadata: Option, + video_pool: Option<&Arc>, +) -> Result { + let raw = &output_frame.data; + let nv12_bytes = &raw.frame; + let width = raw.width; + let height = raw.height; + + let layout = VideoLayout::packed(width, height, PixelFormat::Nv12); + let expected_bytes = layout.total_bytes(); + + if nv12_bytes.len() < expected_bytes { + return Err(format!( + "Vulkan Video decoder returned {len} bytes but NV12 {width}×{height} needs {expected_bytes}", + len = nv12_bytes.len(), + )); + } + + let mut data = video_pool.map_or_else( + || PooledVideoData::from_vec(vec![0u8; expected_bytes]), + |pool| pool.get(expected_bytes), + ); + data.as_mut_slice()[..expected_bytes].copy_from_slice(&nv12_bytes[..expected_bytes]); + + let frame_metadata = metadata.map(|mut m| { + // Propagate PTS from vk-video if the incoming metadata had none. + if m.timestamp_us.is_none() { + m.timestamp_us = output_frame.metadata.pts; + } + m + }); + + Ok(VideoFrame { + data: Arc::new(data), + pixel_format: PixelFormat::Nv12, + width, + height, + layout, + metadata: frame_metadata, + }) +} + +// --------------------------------------------------------------------------- +// Encoder +// --------------------------------------------------------------------------- + +/// Configuration for the Vulkan Video H.264 encoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct VulkanVideoH264EncoderConfig { + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, + /// Target bitrate in bits per second. + pub bitrate: u32, + /// Maximum bitrate in bits per second (VBR mode). + /// Defaults to 4× the target bitrate. + pub max_bitrate: Option, + /// Target framerate (frames per second). + pub framerate: u32, +} + +impl Default for VulkanVideoH264EncoderConfig { + fn default() -> Self { + Self { hw_accel: HwAccelMode::Auto, bitrate: 2_000_000, max_bitrate: None, framerate: 30 } + } +} + +/// Vulkan Video H.264 encoder node. +/// +/// Accepts NV12/I420 `VideoFrame`s on its `"in"` pin and emits H.264 +/// encoded `Binary` packets on its `"out"` pin. +/// +/// Internally uses `vk-video::BytesEncoder` for GPU-accelerated encoding. +/// I420 input is converted to NV12 before encoding since Vulkan Video +/// operates on NV12. +pub struct VulkanVideoH264EncoderNode { + config: VulkanVideoH264EncoderConfig, +} + +impl VulkanVideoH264EncoderNode { + /// Create a new encoder node with the given configuration. + /// + /// # Errors + /// + /// Returns an error if `hw_accel` is `ForceCpu` — this node only + /// supports hardware encoding. Also rejects zero bitrate or + /// framerate to avoid confusing hardware-level errors later. + pub fn new(config: VulkanVideoH264EncoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "VulkanVideoH264EncoderNode only supports hardware encoding; \ + use an OpenH264 encoder for CPU-only mode" + .to_string(), + )); + } + if config.bitrate == 0 { + return Err(StreamKitError::Configuration( + "VulkanVideoH264EncoderNode: bitrate must be > 0".to_string(), + )); + } + if config.framerate == 0 { + return Err(StreamKitError::Configuration( + "VulkanVideoH264EncoderNode: framerate must be > 0".to_string(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for VulkanVideoH264EncoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![ + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::I420, + }), + ], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::H264, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + fn content_type(&self) -> Option { + Some(H264_CONTENT_TYPE.to_string()) + } + + async fn run(self: Box, mut context: NodeContext) -> Result<(), StreamKitError> { + let node_name = context.output_sender.node_name().to_string(); + state_helpers::emit_initializing(&context.state_tx, &node_name); + + tracing::info!( + "VulkanVideoH264EncoderNode starting (hw_accel={:?}, bitrate={})", + self.config.hw_accel, + self.config.bitrate, + ); + let mut input_rx = context.take_input("in")?; + + // ── Metrics ────────────────────────────────────────────────────── + let meter = global::meter("skit_nodes"); + let packets_processed_counter = + meter.u64_counter("vulkan_video_h264_encoder_packets_processed").build(); + let encode_duration_histogram = meter + .f64_histogram("vulkan_video_h264_encode_duration") + .with_boundaries(streamkit_core::metrics::HISTOGRAM_BOUNDARIES_CODEC_PACKET.to_vec()) + .build(); + + // ── Channels ───────────────────────────────────────────────────── + let (encode_tx, mut encode_rx) = + mpsc::channel::<(VideoFrame, Option)>(get_codec_channel_capacity()); + let (result_tx, mut result_rx) = + mpsc::channel::>(get_codec_channel_capacity()); + + // ── Blocking encode task ───────────────────────────────────────── + let config = self.config.clone(); + let encode_task = tokio::task::spawn_blocking(move || { + // Encoder and device are lazily initialised on the first frame + // so we know the actual resolution. + let mut encoder: Option = None; + let mut device: Option> = None; + let mut current_dimensions: Option<(u32, u32)> = None; + + while let Some((frame, metadata)) = encode_rx.blocking_recv() { + if result_tx.is_closed() { + return; + } + + let dims = (frame.width, frame.height); + + // (Re-)create encoder when dimensions change. + if current_dimensions != Some(dims) { + tracing::info!( + "VulkanVideoH264EncoderNode: (re)creating encoder for {}×{}", + dims.0, + dims.1, + ); + + let dev = match init_vulkan_encode_device(device.as_ref()) { + Ok(d) => d, + Err(err) => { + let _ = result_tx.blocking_send(Err(err)); + return; + }, + }; + + let max_bitrate = u64::from( + config.max_bitrate.unwrap_or_else(|| config.bitrate.saturating_mul(4)), + ); + + let output_params = match dev.encoder_output_parameters_high_quality( + vk_video::parameters::RateControl::VariableBitrate { + average_bitrate: u64::from(config.bitrate), + max_bitrate, + virtual_buffer_size: Duration::from_secs(2), + }, + ) { + Ok(p) => p, + Err(err) => { + let _ = result_tx.blocking_send(Err(format!( + "failed to get encoder output parameters: {err}" + ))); + return; + }, + }; + + let width = NonZeroU32::new(dims.0).unwrap_or(NonZeroU32::MIN); + let height = NonZeroU32::new(dims.1).unwrap_or(NonZeroU32::MIN); + + let enc = + match dev.create_bytes_encoder(vk_video::parameters::EncoderParameters { + input_parameters: vk_video::parameters::VideoParameters { + width, + height, + target_framerate: config.framerate.into(), + }, + output_parameters: output_params, + }) { + Ok(e) => e, + Err(err) => { + let _ = result_tx.blocking_send(Err(format!( + "failed to create BytesEncoder: {err}" + ))); + return; + }, + }; + + device = Some(dev); + encoder = Some(enc); + current_dimensions = Some(dims); + } + + let Some(enc) = encoder.as_mut() else { + let _ = result_tx.blocking_send(Err("encoder not initialised".to_string())); + return; + }; + + // Convert I420 → NV12 if necessary. + let nv12_data = match frame.pixel_format { + PixelFormat::Nv12 => frame.data.as_slice().to_vec(), + PixelFormat::I420 => i420_to_nv12(&frame), + other => { + let _ = result_tx.blocking_send(Err(format!( + "VulkanVideoH264EncoderNode: unsupported pixel format {other:?}, \ + expected NV12 or I420" + ))); + continue; + }, + }; + + let force_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); + + let input_frame = vk_video::InputFrame { + data: vk_video::RawFrameData { + frame: nv12_data, + width: frame.width, + height: frame.height, + }, + pts: metadata.as_ref().and_then(|m| m.timestamp_us), + }; + + let encode_start = Instant::now(); + let result = enc.encode(&input_frame, force_keyframe); + encode_duration_histogram.record(encode_start.elapsed().as_secs_f64(), &[]); + + match result { + Ok(encoded_chunk) => { + // Always propagate the keyframe flag, even when + // the input had no metadata. Without this, + // downstream RTMP/MoQ transport cannot detect + // keyframes for stream initialisation. + let out_meta = match metadata { + Some(mut m) => { + m.keyframe = Some(encoded_chunk.is_keyframe); + Some(m) + }, + None => Some(PacketMetadata { + timestamp_us: None, + duration_us: None, + sequence: None, + keyframe: Some(encoded_chunk.is_keyframe), + }), + }; + + let output = EncoderOutput { + data: Bytes::from(encoded_chunk.data), + metadata: out_meta, + }; + if result_tx.blocking_send(Ok(output)).is_err() { + return; + } + }, + Err(err) => { + let _ = result_tx + .blocking_send(Err(format!("Vulkan Video H.264 encode error: {err}"))); + }, + } + } + }); + + // ── State transition ───────────────────────────────────────────── + state_helpers::emit_running(&context.state_tx, &node_name); + let mut stats_tracker = NodeStatsTracker::new(node_name.clone(), context.stats_tx.clone()); + let batch_size = context.batch_size; + + // ── Input task ─────────────────────────────────────────────────── + let encode_tx_clone = encode_tx.clone(); + let node_label = "VulkanVideoH264EncoderNode"; + let mut input_task = tokio::spawn(async move { + loop { + let Some(first_packet) = input_rx.recv().await else { + break; + }; + + let packet_batch = + packet_helpers::batch_packets_greedy(first_packet, &mut input_rx, batch_size); + + for packet in packet_batch { + if let Packet::Video(mut frame) = packet { + let metadata = frame.metadata.take(); + if encode_tx_clone.send((frame, metadata)).await.is_err() { + tracing::error!("{node_label} encode task has shut down unexpectedly"); + return; + } + } + } + } + tracing::info!("{node_label} input stream closed"); + }); + + // ── Forward loop ───────────────────────────────────────────────── + crate::codec_utils::codec_forward_loop( + &mut context, + &mut result_rx, + &mut input_task, + encode_task, + encode_tx, + &packets_processed_counter, + &mut stats_tracker, + |encoded: EncoderOutput| Packet::Binary { + data: encoded.data, + content_type: Some(Cow::Borrowed(H264_CONTENT_TYPE)), + metadata: encoded.metadata, + }, + node_label, + ) + .await; + + state_helpers::emit_stopped(&context.state_tx, &node_name, "input_closed"); + tracing::info!("VulkanVideoH264EncoderNode finished"); + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// Encoder helpers +// --------------------------------------------------------------------------- + +/// Internal encoded output type for the encoder channel. +struct EncoderOutput { + data: Bytes, + metadata: Option, +} + +/// Initialise (or reuse) the Vulkan device for encoding. +fn init_vulkan_encode_device( + existing: Option<&Arc>, +) -> Result, String> { + if let Some(dev) = existing { + return Ok(Arc::clone(dev)); + } + + let instance = vk_video::VulkanInstance::new() + .map_err(|e| format!("failed to create VulkanInstance: {e}"))?; + + let adapter = instance + .create_adapter(&vk_video::parameters::VulkanAdapterDescriptor::default()) + .map_err(|e| format!("failed to create VulkanAdapter: {e}"))?; + + let device = adapter + .create_device(&vk_video::parameters::VulkanDeviceDescriptor::default()) + .map_err(|e| format!("failed to create VulkanDevice: {e}"))?; + + if !device.supports_encoding() { + return Err("Vulkan device does not support video encoding".to_string()); + } + + tracing::info!("Vulkan Video encode device initialised successfully"); + Ok(device) +} + +/// Convert an I420 `VideoFrame` to NV12 byte layout. +/// +/// NV12 layout: Y plane (width × height) followed by interleaved UV plane +/// (width × height/2). +fn i420_to_nv12(frame: &VideoFrame) -> Vec { + let w = frame.width as usize; + let h = frame.height as usize; + let layout = frame.layout(); + + let y_size = w * h; + let uv_size = w * (h / 2); + let mut nv12 = vec![0u8; y_size + uv_size]; + + let src = frame.data.as_slice(); + let planes = layout.planes(); + + let y_plane = &planes[0]; + let u_plane = &planes[1]; + let v_plane = &planes[2]; + + // Copy Y plane. + for row in 0..h { + let src_start = y_plane.offset + row * y_plane.stride; + let dst_start = row * w; + nv12[dst_start..dst_start + w].copy_from_slice(&src[src_start..src_start + w]); + } + + // Interleave U and V into NV12 UV plane. + let chroma_h = h / 2; + let chroma_w = w / 2; + for row in 0..chroma_h { + let u_src_start = u_plane.offset + row * u_plane.stride; + let v_src_start = v_plane.offset + row * v_plane.stride; + let dst_start = y_size + row * w; + for col in 0..chroma_w { + nv12[dst_start + col * 2] = src[u_src_start + col]; + nv12[dst_start + col * 2 + 1] = src[v_src_start + col]; + } + } + + nv12 +} + +// --------------------------------------------------------------------------- +// Registration +// --------------------------------------------------------------------------- + +use schemars::schema_for; +use streamkit_core::registry::StaticPins; + +#[allow(clippy::expect_used, clippy::missing_panics_doc)] +pub fn register_vulkan_video_nodes(registry: &mut NodeRegistry) { + let default_decoder = VulkanVideoH264DecoderNode::new(VulkanVideoH264DecoderConfig::default()) + .expect("default VulkanVideoH264 decoder config should be valid"); + registry.register_static_with_description( + "video::vulkan_video::h264_decoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(VulkanVideoH264DecoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(VulkanVideoH264DecoderConfig)) + .expect("VulkanVideoH264DecoderConfig schema should serialize to JSON"), + StaticPins { inputs: default_decoder.input_pins(), outputs: default_decoder.output_pins() }, + vec!["video".to_string(), "codecs".to_string(), "h264".to_string(), "hw".to_string()], + false, + "Decodes H.264 Annex B packets into raw NV12 video frames using Vulkan Video \ + hardware acceleration. Requires a GPU with Vulkan Video decode support \ + (NVIDIA, AMD, or Intel with recent Mesa drivers). Use video::openh264::decoder \ + for CPU-only fallback.", + ); + + let default_encoder = VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()) + .expect("default VulkanVideoH264 encoder config should be valid"); + registry.register_static_with_description( + "video::vulkan_video::h264_encoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(VulkanVideoH264EncoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(VulkanVideoH264EncoderConfig)) + .expect("VulkanVideoH264EncoderConfig schema should serialize to JSON"), + StaticPins { inputs: default_encoder.input_pins(), outputs: default_encoder.output_pins() }, + vec!["video".to_string(), "codecs".to_string(), "h264".to_string(), "hw".to_string()], + false, + "Encodes raw video frames (NV12 or I420) into H.264 Annex B packets using \ + Vulkan Video hardware acceleration. Supports VBR rate control with configurable \ + bitrate. Requires a GPU with Vulkan Video encode support. Use \ + video::openh264::encoder for CPU-only fallback.", + ); +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::disallowed_macros)] +mod tests { + use super::*; + use crate::test_utils::{ + assert_state_initializing, assert_state_running, assert_state_stopped, create_test_context, + create_test_video_frame, + }; + use std::collections::HashMap; + use streamkit_core::types::Packet; + use tokio::sync::mpsc; + + // ── Vulkan Video availability helper ──────────────────────────────── + // + // Integration tests that require a Vulkan Video capable GPU use this + // helper. On machines without the right hardware/drivers the tests + // print a message and pass (skip) instead of failing. + + /// Try to create a Vulkan Video device. Returns `true` if both encode + /// and decode are available. + fn vulkan_video_available() -> bool { + let Ok(instance) = vk_video::VulkanInstance::new() else { + return false; + }; + let Ok(adapter) = + instance.create_adapter(&vk_video::parameters::VulkanAdapterDescriptor::default()) + else { + return false; + }; + let Ok(device) = + adapter.create_device(&vk_video::parameters::VulkanDeviceDescriptor::default()) + else { + return false; + }; + device.supports_decoding() && device.supports_encoding() + } + + /// Like [`vulkan_video_available`] but only checks for decode support. + fn vulkan_decode_available() -> bool { + let Ok(instance) = vk_video::VulkanInstance::new() else { + return false; + }; + let Ok(adapter) = + instance.create_adapter(&vk_video::parameters::VulkanAdapterDescriptor::default()) + else { + return false; + }; + let Ok(device) = + adapter.create_device(&vk_video::parameters::VulkanDeviceDescriptor::default()) + else { + return false; + }; + device.supports_decoding() + } + + /// Like [`vulkan_video_available`] but only checks for encode support. + fn vulkan_encode_available() -> bool { + let Ok(instance) = vk_video::VulkanInstance::new() else { + return false; + }; + let Ok(adapter) = + instance.create_adapter(&vk_video::parameters::VulkanAdapterDescriptor::default()) + else { + return false; + }; + let Ok(device) = + adapter.create_device(&vk_video::parameters::VulkanDeviceDescriptor::default()) + else { + return false; + }; + device.supports_encoding() + } + + macro_rules! skip_without_vulkan_encode { + () => { + if !vulkan_encode_available() { + eprintln!("SKIPPED: no Vulkan Video encode support on this machine"); + return; + } + }; + } + + macro_rules! skip_without_vulkan_decode { + () => { + if !vulkan_decode_available() { + eprintln!("SKIPPED: no Vulkan Video decode support on this machine"); + return; + } + }; + } + + macro_rules! skip_without_vulkan_video { + () => { + if !vulkan_video_available() { + eprintln!("SKIPPED: no Vulkan Video encode+decode support on this machine"); + return; + } + }; + } + + // ── Config validation tests (no GPU needed) ───────────────────────── + + #[test] + fn test_decoder_rejects_force_cpu() { + let result = VulkanVideoH264DecoderNode::new(VulkanVideoH264DecoderConfig { + hw_accel: HwAccelMode::ForceCpu, + }); + assert!(result.is_err(), "ForceCpu should be rejected for HW-only decoder"); + } + + #[test] + fn test_decoder_accepts_auto() { + let result = VulkanVideoH264DecoderNode::new(VulkanVideoH264DecoderConfig { + hw_accel: HwAccelMode::Auto, + }); + assert!(result.is_ok(), "Auto should be accepted"); + } + + #[test] + fn test_decoder_accepts_force_hw() { + let result = VulkanVideoH264DecoderNode::new(VulkanVideoH264DecoderConfig { + hw_accel: HwAccelMode::ForceHw, + }); + assert!(result.is_ok(), "ForceHw should be accepted"); + } + + #[test] + fn test_encoder_rejects_force_cpu() { + let result = VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig { + hw_accel: HwAccelMode::ForceCpu, + ..Default::default() + }); + assert!(result.is_err(), "ForceCpu should be rejected for HW-only encoder"); + } + + #[test] + fn test_encoder_rejects_zero_bitrate() { + let result = VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig { + bitrate: 0, + ..Default::default() + }); + assert!(result.is_err(), "bitrate=0 should be rejected"); + } + + #[test] + fn test_encoder_rejects_zero_framerate() { + let result = VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig { + framerate: 0, + ..Default::default() + }); + assert!(result.is_err(), "framerate=0 should be rejected"); + } + + #[test] + fn test_encoder_accepts_valid_config() { + let result = VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig { + hw_accel: HwAccelMode::Auto, + bitrate: 2_000_000, + max_bitrate: None, + framerate: 30, + }); + assert!(result.is_ok(), "valid config should be accepted"); + } + + #[test] + fn test_encoder_accepts_custom_max_bitrate() { + let result = VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig { + hw_accel: HwAccelMode::Auto, + bitrate: 2_000_000, + max_bitrate: Some(8_000_000), + framerate: 60, + }); + assert!(result.is_ok(), "custom max_bitrate config should be accepted"); + } + + // ── deny_unknown_fields tests ───────────────────────────────────── + + #[test] + fn test_deny_unknown_fields_decoder() { + let json = r#"{"hw_accel":"auto","bogus_field":42}"#; + let result: Result = serde_json::from_str(json); + assert!(result.is_err(), "Unknown fields should be rejected"); + } + + #[test] + fn test_deny_unknown_fields_encoder() { + let json = r#"{"bitrate":1000000,"unknown_key":"oops"}"#; + let result: Result = serde_json::from_str(json); + assert!(result.is_err(), "Unknown fields should be rejected"); + } + + // ── Pin configuration tests ───────────────────────────────────────── + + #[test] + fn test_decoder_pin_config() { + let node = + VulkanVideoH264DecoderNode::new(VulkanVideoH264DecoderConfig::default()).unwrap(); + + let inputs = node.input_pins(); + assert_eq!(inputs.len(), 1); + assert_eq!(inputs[0].name, "in"); + assert!(matches!(inputs[0].cardinality, PinCardinality::One)); + assert!(matches!( + &inputs[0].accepts_types[0], + PacketType::EncodedVideo(fmt) if fmt.codec == VideoCodec::H264 + )); + + let outputs = node.output_pins(); + assert_eq!(outputs.len(), 1); + assert_eq!(outputs[0].name, "out"); + assert!(matches!(outputs[0].cardinality, PinCardinality::Broadcast)); + assert!(matches!( + &outputs[0].produces_type, + PacketType::RawVideo(fmt) if fmt.pixel_format == PixelFormat::Nv12 + )); + } + + #[test] + fn test_encoder_pin_config() { + let node = + VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()).unwrap(); + + let inputs = node.input_pins(); + assert_eq!(inputs.len(), 1); + assert_eq!(inputs[0].name, "in"); + assert_eq!(inputs[0].accepts_types.len(), 2, "should accept NV12 and I420"); + + let outputs = node.output_pins(); + assert_eq!(outputs.len(), 1); + assert_eq!(outputs[0].name, "out"); + assert!(matches!( + &outputs[0].produces_type, + PacketType::EncodedVideo(fmt) if fmt.codec == VideoCodec::H264 + )); + } + + #[test] + fn test_encoder_content_type() { + let node = + VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()).unwrap(); + assert_eq!( + node.content_type().as_deref(), + Some(H264_CONTENT_TYPE), + "Encoder should report video/h264 content type" + ); + } + + // ── Integration tests (require Vulkan Video GPU) ──────────────────── + + #[tokio::test] + async fn test_vulkan_video_encode_nv12() { + skip_without_vulkan_encode!(); + + let (input_tx, input_rx) = mpsc::channel(10); + let mut inputs = HashMap::new(); + inputs.insert("in".to_string(), input_rx); + + let (context, sender, mut state_rx) = create_test_context(inputs, 10); + let encoder = + VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()).unwrap(); + + let handle = tokio::spawn(async move { Box::new(encoder).run(context).await }); + + assert_state_initializing(&mut state_rx).await; + assert_state_running(&mut state_rx).await; + + for i in 0_u64..5 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(33_333 * i), + duration_us: Some(33_333), + sequence: Some(i), + keyframe: Some(i == 0), + }); + input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(input_tx); + + assert_state_stopped(&mut state_rx).await; + handle.await.unwrap().unwrap(); + + let packets = sender.get_packets_for_pin("out").await; + assert!(!packets.is_empty(), "Vulkan Video encoder should produce packets"); + + for (i, packet) in packets.iter().enumerate() { + match packet { + Packet::Binary { data, content_type, metadata, .. } => { + assert!(!data.is_empty(), "Encoded packet {i} should have data"); + assert_eq!( + content_type.as_deref(), + Some(H264_CONTENT_TYPE), + "Content type should be video/h264" + ); + assert!(metadata.is_some(), "Encoded packet {i} should have metadata"); + let meta = metadata.as_ref().unwrap(); + assert!( + meta.keyframe.is_some(), + "Encoded packet {i} should have keyframe flag" + ); + }, + _ => panic!("Expected Binary packet from Vulkan Video encoder, got {packet:?}"), + } + } + } + + #[tokio::test] + async fn test_vulkan_video_encode_i420() { + skip_without_vulkan_encode!(); + + let (input_tx, input_rx) = mpsc::channel(10); + let mut inputs = HashMap::new(); + inputs.insert("in".to_string(), input_rx); + + let (context, sender, mut state_rx) = create_test_context(inputs, 10); + let encoder = + VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()).unwrap(); + + let handle = tokio::spawn(async move { Box::new(encoder).run(context).await }); + + assert_state_initializing(&mut state_rx).await; + assert_state_running(&mut state_rx).await; + + for i in 0_u64..3 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::I420, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(33_333 * i), + duration_us: Some(33_333), + sequence: Some(i), + keyframe: Some(true), + }); + input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(input_tx); + + assert_state_stopped(&mut state_rx).await; + handle.await.unwrap().unwrap(); + + let packets = sender.get_packets_for_pin("out").await; + assert!(!packets.is_empty(), "Vulkan Video encoder should produce packets from I420 input"); + } + + #[tokio::test] + async fn test_vulkan_video_encode_metadata_without_input_metadata() { + skip_without_vulkan_encode!(); + + let (input_tx, input_rx) = mpsc::channel(10); + let mut inputs = HashMap::new(); + inputs.insert("in".to_string(), input_rx); + + let (context, sender, mut state_rx) = create_test_context(inputs, 10); + let encoder = + VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()).unwrap(); + + let handle = tokio::spawn(async move { Box::new(encoder).run(context).await }); + + assert_state_initializing(&mut state_rx).await; + assert_state_running(&mut state_rx).await; + + // Send frames with NO metadata to verify keyframe flag is still propagated. + for _ in 0..3 { + let frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + // frame.metadata is None by default from create_test_video_frame + input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(input_tx); + + assert_state_stopped(&mut state_rx).await; + handle.await.unwrap().unwrap(); + + let packets = sender.get_packets_for_pin("out").await; + assert!(!packets.is_empty(), "Encoder should produce packets even without input metadata"); + + for (i, packet) in packets.iter().enumerate() { + match packet { + Packet::Binary { metadata, .. } => { + assert!( + metadata.is_some(), + "Packet {i} should have metadata even when input had None" + ); + let meta = metadata.as_ref().unwrap(); + assert!( + meta.keyframe.is_some(), + "Packet {i} should always have keyframe flag set" + ); + }, + _ => panic!("Expected Binary packet"), + } + } + } + + #[tokio::test] + async fn test_vulkan_video_roundtrip_encode_decode() { + skip_without_vulkan_video!(); + + // ── Step 1: Encode NV12 frames to H.264 ───────────────────────── + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder = + VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()).unwrap(); + + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + let frame_count = 5_u64; + let width = 64_u32; + let height = 64_u32; + + for i in 0..frame_count { + let mut frame = create_test_video_frame(width, height, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(33_333 * i), + duration_us: Some(33_333), + sequence: Some(i), + keyframe: Some(i == 0), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty(), "Encoder should produce packets"); + + // ── Step 2: Decode the H.264 packets back to NV12 ─────────────── + let (dec_input_tx, dec_input_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_input_rx); + + let (dec_context, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = + VulkanVideoH264DecoderNode::new(VulkanVideoH264DecoderConfig::default()).unwrap(); + + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_context).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + // Feed encoded packets to the decoder. + for packet in encoded_packets { + dec_input_tx.send(packet).await.unwrap(); + } + drop(dec_input_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "Decoder should produce frames from roundtrip data"); + + // Verify decoded frames are NV12 with the right dimensions. + for (i, packet) in decoded_packets.iter().enumerate() { + match packet { + Packet::Video(frame) => { + assert_eq!( + frame.pixel_format, + PixelFormat::Nv12, + "Decoded frame {i} should be NV12" + ); + assert_eq!(frame.width, width, "Decoded frame {i} width mismatch"); + assert_eq!(frame.height, height, "Decoded frame {i} height mismatch"); + assert!( + !frame.data.as_slice().is_empty(), + "Decoded frame {i} should have data" + ); + }, + _ => panic!("Expected Video packet from decoder, got {packet:?}"), + } + } + } + + // ── I420→NV12 conversion unit test ────────────────────────────────── + + #[test] + fn test_i420_to_nv12_conversion() { + let width = 4_u32; + let height = 4_u32; + let frame = create_test_video_frame(width, height, PixelFormat::I420, 0); + + // Manually fill planes with known values for verification. + let layout = frame.layout(); + let planes = layout.planes(); + + // Build a frame with identifiable plane content. + let mut data = vec![0u8; layout.total_bytes()]; + // Y plane: fill with 100 + for row in 0..height as usize { + for col in 0..width as usize { + data[planes[0].offset + row * planes[0].stride + col] = 100; + } + } + // U plane: fill with 50 + let chroma_w = width as usize / 2; + let chroma_h = height as usize / 2; + for row in 0..chroma_h { + for col in 0..chroma_w { + data[planes[1].offset + row * planes[1].stride + col] = 50; + } + } + // V plane: fill with 200 + for row in 0..chroma_h { + for col in 0..chroma_w { + data[planes[2].offset + row * planes[2].stride + col] = 200; + } + } + + let test_frame = VideoFrame::new(width, height, PixelFormat::I420, data) + .expect("test frame should be valid"); + + let nv12 = i420_to_nv12(&test_frame); + + let y_size = (width * height) as usize; + let uv_size = width as usize * (height as usize / 2); + assert_eq!(nv12.len(), y_size + uv_size, "NV12 buffer size mismatch"); + + // Verify Y plane was copied correctly. + for (i, &byte) in nv12.iter().enumerate().take(y_size) { + assert_eq!(byte, 100, "Y plane byte {i} mismatch"); + } + + // Verify UV plane has interleaved U and V values. + for row in 0..chroma_h { + for col in 0..chroma_w { + let uv_offset = y_size + row * width as usize + col * 2; + assert_eq!(nv12[uv_offset], 50, "U value at row={row} col={col} mismatch"); + assert_eq!(nv12[uv_offset + 1], 200, "V value at row={row} col={col} mismatch"); + } + } + } + + // ── Standalone decode test (requires encode+decode to produce input) ─ + + #[tokio::test] + async fn test_vulkan_video_decode_produces_frames() { + // We need both encode (to generate H.264 data) and decode capabilities. + // Use skip_without_vulkan_decode for the decode-specific skip message, + // but we also need encode to produce test data. + skip_without_vulkan_decode!(); + skip_without_vulkan_encode!(); + + // First encode a few frames to get valid H.264 data. + let (enc_tx, enc_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_rx); + + let (enc_ctx, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder = + VulkanVideoH264EncoderNode::new(VulkanVideoH264EncoderConfig::default()).unwrap(); + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_ctx).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + for i in 0_u64..5 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(33_333 * i), + duration_us: Some(33_333), + sequence: Some(i), + keyframe: Some(i == 0), + }); + enc_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty(), "Need encoded data to test decoder"); + + // Now decode. + let (dec_tx, dec_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_rx); + + let (dec_ctx, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = + VulkanVideoH264DecoderNode::new(VulkanVideoH264DecoderConfig::default()).unwrap(); + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_ctx).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + for packet in encoded_packets { + dec_tx.send(packet).await.unwrap(); + } + drop(dec_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "Decoder should produce NV12 frames"); + + for (i, packet) in decoded_packets.iter().enumerate() { + match packet { + Packet::Video(frame) => { + assert_eq!( + frame.pixel_format, + PixelFormat::Nv12, + "Decoded frame {i} should be NV12" + ); + assert_eq!(frame.width, 64, "Decoded frame {i} width mismatch"); + assert_eq!(frame.height, 64, "Decoded frame {i} height mismatch"); + }, + _ => panic!("Expected Video packet from decoder"), + } + } + } + + // ── Registration test ─────────────────────────────────────────────── + + #[test] + fn test_node_registration() { + let mut registry = NodeRegistry::new(); + register_vulkan_video_nodes(&mut registry); + + // Verify both nodes are registered by trying to create them with + // default config. + assert!( + registry.create_node("video::vulkan_video::h264_decoder", None).is_ok(), + "decoder should be registered" + ); + assert!( + registry.create_node("video::vulkan_video::h264_encoder", None).is_ok(), + "encoder should be registered" + ); + } +} diff --git a/justfile b/justfile index daa2eafd..2ae3ebf7 100644 --- a/justfile +++ b/justfile @@ -216,11 +216,12 @@ test-skit: @cargo test --workspace -- --skip gpu_tests:: @cargo test -p streamkit-server --features "moq" -# Run GPU compositor tests (requires a machine with a GPU) +# Run GPU tests (requires a machine with a GPU) test-skit-gpu: @echo "Testing skit (GPU)..." @cargo test -p streamkit-nodes --features gpu @cargo test -p streamkit-engine --features gpu + @cargo test -p streamkit-nodes --features nvcodec # Lint and format check the skit code # Note: We exclude dhat-heap since it's mutually exclusive with profiling (both define global allocators) From 5bba936e6912c752b30120f748713c65938c424f Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 13:46:33 +0000 Subject: [PATCH 02/69] ci: run nvcodec tests on GPU runner The self-hosted GPU runner (skit-demo-eu-gpu) has an NVIDIA GPU but the CI workflow wasn't exercising the nvcodec feature tests. Add the missing cargo test invocation so NVENC/NVDEC AV1 tests run alongside the existing GPU compositor tests. Signed-off-by: Devin AI Co-Authored-By: Claudio Costa --- .github/workflows/skit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/skit.yml b/.github/workflows/skit.yml index da6bdf34..2c3bcab6 100644 --- a/.github/workflows/skit.yml +++ b/.github/workflows/skit.yml @@ -143,6 +143,7 @@ jobs: run: | cargo test --locked -p streamkit-nodes --features gpu cargo test --locked -p streamkit-engine --features gpu + cargo test --locked -p streamkit-nodes --features nvcodec build: name: Build From ea842e68c6421c4ebfb3b90507c2bd2670fbc203 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 13:51:07 +0000 Subject: [PATCH 03/69] ci: install CUDA headers on GPU runner for nvcodec tests The shiguredo_nvcodec build script requires cuda.h at compile time. Install nvidia-cuda-toolkit on the self-hosted GPU runner if CUDA headers aren't already present. Signed-off-by: Devin AI Co-Authored-By: Claudio Costa --- .github/workflows/skit.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/skit.yml b/.github/workflows/skit.yml index 2c3bcab6..2a3502b0 100644 --- a/.github/workflows/skit.yml +++ b/.github/workflows/skit.yml @@ -120,7 +120,14 @@ jobs: - uses: actions/checkout@v5 - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libvpx-dev nasm cmake pkg-config libopus-dev + run: | + sudo apt-get update + sudo apt-get install -y libvpx-dev nasm cmake pkg-config libopus-dev + # CUDA headers needed by shiguredo_nvcodec (nvcodec feature). + # Only install if not already present. + if [ ! -f /usr/local/cuda/include/cuda.h ]; then + sudo apt-get install -y nvidia-cuda-toolkit + fi - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master From 90090fb8f663a5c9f6b4f63f896fde6d3ef803ec Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 13:55:30 +0000 Subject: [PATCH 04/69] ci: set CUDA_INCLUDE_PATH for nvcodec build on GPU runner Ubuntu's nvidia-cuda-toolkit installs cuda.h to /usr/include, but shiguredo_nvcodec's build script defaults to /usr/local/cuda/include. Set CUDA_INCLUDE_PATH=/usr/include so the build finds the headers. Signed-off-by: Devin AI Co-Authored-By: Claudio Costa --- .github/workflows/skit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/skit.yml b/.github/workflows/skit.yml index 2a3502b0..848fcb2b 100644 --- a/.github/workflows/skit.yml +++ b/.github/workflows/skit.yml @@ -147,6 +147,9 @@ jobs: VPX_LIB_DIR: /usr/lib/x86_64-linux-gnu VPX_INCLUDE_DIR: /usr/include VPX_VERSION: "1.13.0" + # Ubuntu's nvidia-cuda-toolkit installs headers to /usr/include, not + # /usr/local/cuda/include. Tell shiguredo_nvcodec where to find them. + CUDA_INCLUDE_PATH: /usr/include run: | cargo test --locked -p streamkit-nodes --features gpu cargo test --locked -p streamkit-engine --features gpu From 1c5924014c4d5459efa94822accae835e30691f3 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 14:35:24 +0000 Subject: [PATCH 05/69] ci: fix nvcodec build on GPU runner (BINDGEN_EXTRA_CLANG_ARGS) Remove conditional nvidia-cuda-toolkit install (already pre-installed on the self-hosted runner) and add BINDGEN_EXTRA_CLANG_ARGS to point bindgen at the LLVM 18 clang builtin includes so stddef.h is found. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- .github/workflows/skit.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/skit.yml b/.github/workflows/skit.yml index 848fcb2b..a0e00159 100644 --- a/.github/workflows/skit.yml +++ b/.github/workflows/skit.yml @@ -123,11 +123,6 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libvpx-dev nasm cmake pkg-config libopus-dev - # CUDA headers needed by shiguredo_nvcodec (nvcodec feature). - # Only install if not already present. - if [ ! -f /usr/local/cuda/include/cuda.h ]; then - sudo apt-get install -y nvidia-cuda-toolkit - fi - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master @@ -150,6 +145,9 @@ jobs: # Ubuntu's nvidia-cuda-toolkit installs headers to /usr/include, not # /usr/local/cuda/include. Tell shiguredo_nvcodec where to find them. CUDA_INCLUDE_PATH: /usr/include + # bindgen (used by shiguredo_nvcodec) needs the clang builtin include + # path so it can find stddef.h and other compiler-provided headers. + BINDGEN_EXTRA_CLANG_ARGS: "-I/usr/lib/llvm-18/lib/clang/18/include" run: | cargo test --locked -p streamkit-nodes --features gpu cargo test --locked -p streamkit-engine --features gpu From 61ed289aa8c9597252aa1ba072e45b47cba99ce8 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 14:38:44 +0000 Subject: [PATCH 06/69] ci: reorder GPU tests so nvcodec runs before engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The streamkit-engine GPU test binary segfaults (SIGSEGV) during cleanup after all 25 tests pass — this is a pre-existing issue likely related to wgpu/Vulkan teardown. Move the nvcodec node tests before the engine GPU tests so they are not blocked by the crash. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- .github/workflows/skit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/skit.yml b/.github/workflows/skit.yml index a0e00159..a241eb9e 100644 --- a/.github/workflows/skit.yml +++ b/.github/workflows/skit.yml @@ -150,8 +150,8 @@ jobs: BINDGEN_EXTRA_CLANG_ARGS: "-I/usr/lib/llvm-18/lib/clang/18/include" run: | cargo test --locked -p streamkit-nodes --features gpu - cargo test --locked -p streamkit-engine --features gpu cargo test --locked -p streamkit-nodes --features nvcodec + cargo test --locked -p streamkit-engine --features gpu build: name: Build From 3dffae7cb4cd41b55fa1ab5817228e4c60b4b500 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 14:43:10 +0000 Subject: [PATCH 07/69] fix(nodes): add missing framerate field in nvcodec test The force_cpu_encoder_rejected test was constructing NvAv1EncoderConfig with all fields explicitly but missed the new framerate field added in the review-fix round. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/nv_av1.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/nodes/src/video/nv_av1.rs b/crates/nodes/src/video/nv_av1.rs index 2de6c8b7..a5cc9177 100644 --- a/crates/nodes/src/video/nv_av1.rs +++ b/crates/nodes/src/video/nv_av1.rs @@ -818,6 +818,7 @@ mod tests { hw_accel: HwAccelMode::ForceCpu, cuda_device: None, bitrate: 2_000_000, + framerate: 30, keyframe_interval: None, }); assert!(result.is_err(), "ForceCpu should be rejected by NV encoder"); From 182e010665a6e3bad5e86b3aefdb9643f0777ec2 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 15:53:55 +0000 Subject: [PATCH 08/69] fix(nodes): register HW codec nodes, fix i420_to_nv12 truncation, remove dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add cfg-gated registration calls for vulkan_video, vaapi, and nvcodec nodes in register_video_nodes() — without these, users enabling the features would get 'node not found' errors at runtime. - Fix i420_to_nv12 in vulkan_video.rs to use div_ceil(2) for chroma dimensions instead of truncating integer division (h/2, w/2), matching the correct implementation in nv_av1.rs. - Update HwAccelMode::Auto doc comment to accurately reflect that HW-only nodes do not implement CPU fallback — Auto and ForceHw behave identically; CPU fallback is achieved by selecting a different (software) node at the pipeline level. - Remove dead default_quality() and default_framerate() functions in vaapi_av1.rs (unused — the struct uses a manual Default impl). - Add registration regression tests to nv_av1 and vaapi_av1 modules. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/mod.rs | 16 +++++++++++++++- crates/nodes/src/video/nv_av1.rs | 17 +++++++++++++++++ crates/nodes/src/video/vaapi_av1.rs | 25 +++++++++++++++++-------- crates/nodes/src/video/vulkan_video.rs | 10 +++++----- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/crates/nodes/src/video/mod.rs b/crates/nodes/src/video/mod.rs index 6230541f..43f51323 100644 --- a/crates/nodes/src/video/mod.rs +++ b/crates/nodes/src/video/mod.rs @@ -84,7 +84,12 @@ pub const H264_CONTENT_TYPE: &str = "video/h264"; )] #[serde(rename_all = "lowercase")] pub enum HwAccelMode { - /// Auto-detect: use HW if available, fall back to CPU otherwise. + /// Auto-detect: attempt hardware acceleration. + /// + /// For HW-only nodes (Vulkan Video, VA-API, NVENC/NVDEC) this behaves + /// identically to `ForceHw` — the node will fail if the required + /// hardware is unavailable. CPU fallback is achieved by selecting a + /// different (software) node at the pipeline level. #[default] Auto, /// Force HW acceleration — fail if unavailable. @@ -631,4 +636,13 @@ pub fn register_video_nodes(registry: &mut NodeRegistry, constraints: &GlobalNod #[cfg(feature = "dav1d")] dav1d::register_dav1d_nodes(registry); + + #[cfg(feature = "vulkan_video")] + vulkan_video::register_vulkan_video_nodes(registry); + + #[cfg(feature = "vaapi")] + vaapi_av1::register_vaapi_av1_nodes(registry); + + #[cfg(feature = "nvcodec")] + nv_av1::register_nv_av1_nodes(registry); } diff --git a/crates/nodes/src/video/nv_av1.rs b/crates/nodes/src/video/nv_av1.rs index a5cc9177..d26e316e 100644 --- a/crates/nodes/src/video/nv_av1.rs +++ b/crates/nodes/src/video/nv_av1.rs @@ -1182,4 +1182,21 @@ mod tests { } } } + + // ── Registration test ──────────────────────────────────────────────── + + #[test] + fn test_node_registration() { + let mut registry = NodeRegistry::new(); + register_nv_av1_nodes(&mut registry); + + assert!( + registry.create_node("video::nv::av1_decoder", None).is_ok(), + "NV AV1 decoder should be registered" + ); + assert!( + registry.create_node("video::nv::av1_encoder", None).is_ok(), + "NV AV1 encoder should be registered" + ); + } } diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 2d1be2bb..9d8e1093 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -747,14 +747,6 @@ pub struct VaapiAv1EncoderConfig { pub hw_accel: HwAccelMode, } -const fn default_quality() -> u32 { - DEFAULT_QUALITY -} - -const fn default_framerate() -> u32 { - DEFAULT_FRAMERATE -} - impl Default for VaapiAv1EncoderConfig { fn default() -> Self { Self { @@ -1804,4 +1796,21 @@ mod tests { let result: Result = serde_json::from_str(json); assert!(result.is_err(), "Unknown fields should be rejected"); } + + // ── Registration test ──────────────────────────────────────────────── + + #[test] + fn test_node_registration() { + let mut registry = NodeRegistry::new(); + register_vaapi_av1_nodes(&mut registry); + + assert!( + registry.create_node("video::vaapi::av1_decoder", None).is_ok(), + "VA-API AV1 decoder should be registered" + ); + assert!( + registry.create_node("video::vaapi::av1_encoder", None).is_ok(), + "VA-API AV1 encoder should be registered" + ); + } } diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs index e0321da6..59390cb6 100644 --- a/crates/nodes/src/video/vulkan_video.rs +++ b/crates/nodes/src/video/vulkan_video.rs @@ -737,9 +737,11 @@ fn i420_to_nv12(frame: &VideoFrame) -> Vec { let h = frame.height as usize; let layout = frame.layout(); + let chroma_w = w.div_ceil(2); + let chroma_h = h.div_ceil(2); + let uv_row_bytes = chroma_w * 2; let y_size = w * h; - let uv_size = w * (h / 2); - let mut nv12 = vec![0u8; y_size + uv_size]; + let mut nv12 = vec![0u8; y_size + uv_row_bytes * chroma_h]; let src = frame.data.as_slice(); let planes = layout.planes(); @@ -756,12 +758,10 @@ fn i420_to_nv12(frame: &VideoFrame) -> Vec { } // Interleave U and V into NV12 UV plane. - let chroma_h = h / 2; - let chroma_w = w / 2; for row in 0..chroma_h { let u_src_start = u_plane.offset + row * u_plane.stride; let v_src_start = v_plane.offset + row * v_plane.stride; - let dst_start = y_size + row * w; + let dst_start = y_size + row * uv_row_bytes; for col in 0..chroma_w { nv12[dst_start + col * 2] = src[u_src_start + col]; nv12[dst_start + col * 2 + 1] = src[v_src_start + col]; From 98c4c4e00060c17752d4afdf04c7aecd1373a74d Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 16:52:43 +0000 Subject: [PATCH 09/69] fix(nodes): add encoder flush comment, validate cuda_device, use GBM plane offsets - vulkan_video.rs: document that vk-video 0.3.0 BytesEncoder has no flush() method (unlike BytesDecoder); frame-at-a-time, no B-frames - nv_av1.rs: reject cuda_device > i32::MAX at construction time instead of silently wrapping via 'as i32' cast - vaapi_av1.rs: use gbm_frame.get_plane_offset() for FrameLayout instead of manually computing y_stride * coded_height; also fix stride fallback to use coded_width instead of display width Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/nv_av1.rs | 30 ++++++++++++++++++++++++++ crates/nodes/src/video/vaapi_av1.rs | 17 ++++++++++----- crates/nodes/src/video/vulkan_video.rs | 8 +++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/crates/nodes/src/video/nv_av1.rs b/crates/nodes/src/video/nv_av1.rs index d26e316e..bff41404 100644 --- a/crates/nodes/src/video/nv_av1.rs +++ b/crates/nodes/src/video/nv_av1.rs @@ -92,6 +92,13 @@ impl NvAv1DecoderNode { .to_string(), )); } + if config.cuda_device.is_some_and(|d| d > i32::MAX as u32) { + return Err(StreamKitError::Configuration(format!( + "cuda_device {} exceeds maximum CUDA device index ({})", + config.cuda_device.unwrap_or(0), + i32::MAX, + ))); + } Ok(Self { config }) } } @@ -424,6 +431,13 @@ impl NvAv1EncoderNode { .to_string(), )); } + if config.cuda_device.is_some_and(|d| d > i32::MAX as u32) { + return Err(StreamKitError::Configuration(format!( + "cuda_device {} exceeds maximum CUDA device index ({})", + config.cuda_device.unwrap_or(0), + i32::MAX, + ))); + } Ok(Self { config }) } } @@ -830,6 +844,22 @@ mod tests { assert!(NvAv1EncoderNode::new(NvAv1EncoderConfig::default()).is_ok()); } + #[test] + fn rejects_cuda_device_exceeding_i32_max() { + let bad_device = i32::MAX as u32 + 1; + let dec_result = NvAv1DecoderNode::new(NvAv1DecoderConfig { + cuda_device: Some(bad_device), + ..NvAv1DecoderConfig::default() + }); + assert!(dec_result.is_err(), "cuda_device > i32::MAX should be rejected by decoder"); + + let enc_result = NvAv1EncoderNode::new(NvAv1EncoderConfig { + cuda_device: Some(bad_device), + ..NvAv1EncoderConfig::default() + }); + assert!(enc_result.is_err(), "cuda_device > i32::MAX should be rejected by encoder"); + } + #[test] fn decoder_pins_correct() { let node = NvAv1DecoderNode::new(NvAv1DecoderConfig::default()).unwrap(); diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 9d8e1093..adc6129c 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -950,20 +950,27 @@ impl StandardVideoEncoder for VaapiAv1Encoder { let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); + // Use actual GBM plane offsets instead of computing them manually. + // Different drivers may place the UV plane at an offset that differs + // from `y_stride * coded_height` (e.g. with extra padding rows). + let offsets = gbm_frame.get_plane_offset(); + let frame_layout = FrameLayout { format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR size: CrosResolution { width: self.coded_width, height: self.coded_height }, planes: vec![ PlaneLayout { buffer_index: 0, - offset: 0, - stride: pitches.first().copied().unwrap_or(self.width as usize), + offset: offsets.first().copied().unwrap_or(0), + stride: pitches.first().copied().unwrap_or(self.coded_width as usize), }, PlaneLayout { buffer_index: 0, - offset: pitches.first().copied().unwrap_or(self.width as usize) - * self.coded_height as usize, - stride: pitches.get(1).copied().unwrap_or(self.width as usize), + offset: offsets.get(1).copied().unwrap_or( + pitches.first().copied().unwrap_or(self.coded_width as usize) + * self.coded_height as usize, + ), + stride: pitches.get(1).copied().unwrap_or(self.coded_width as usize), }, ], }; diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs index 59390cb6..df516eff 100644 --- a/crates/nodes/src/video/vulkan_video.rs +++ b/crates/nodes/src/video/vulkan_video.rs @@ -635,6 +635,14 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { }, } } + + // Note: vk-video 0.3.0's BytesEncoder has no flush() method + // (unlike BytesDecoder which does). The encoder operates + // frame-at-a-time without B-frame reordering, so no frames + // should be buffered internally. If a future vk-video version + // adds flush(), it should be called here — matching the + // decoder's flush at line ~245 and the pattern in + // encoder_trait::spawn_standard_encode_task. }); // ── State transition ───────────────────────────────────────────── From 25828bf5ca1754e8e6169348f8c22a2de1ee2c18 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 17:00:23 +0000 Subject: [PATCH 10/69] fix(skit): forward HW codec feature flags from streamkit-server to streamkit-nodes Without these forwarding features, `just extra_features="--features vulkan_video" skit` would silently ignore the feature since streamkit-server didn't know about it. Adds vulkan_video, vaapi, and nvcodec feature forwarding, matching the existing pattern for svt_av1 and dav1d. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- apps/skit/Cargo.toml | 3 +++ justfile | 2 ++ 2 files changed, 5 insertions(+) diff --git a/apps/skit/Cargo.toml b/apps/skit/Cargo.toml index 81efe5c0..98b540c1 100644 --- a/apps/skit/Cargo.toml +++ b/apps/skit/Cargo.toml @@ -152,6 +152,9 @@ svt_av1 = ["streamkit-nodes/svt_av1"] svt_av1_static = ["streamkit-nodes/svt_av1_static"] dav1d = ["streamkit-nodes/dav1d"] dav1d_static = ["streamkit-nodes/dav1d_static"] +vulkan_video = ["streamkit-nodes/vulkan_video"] +vaapi = ["streamkit-nodes/vaapi"] +nvcodec = ["streamkit-nodes/nvcodec"] [dev-dependencies] tokio-test = "0.4.5" diff --git a/justfile b/justfile index 2ae3ebf7..967f9ba6 100644 --- a/justfile +++ b/justfile @@ -11,6 +11,8 @@ tokio_console_features := "--features tokio-console" # Optional extra features to enable in skit builds (e.g. "svt_av1"). # Usage: just extra_features="--features svt_av1" skit # or: just extra_features="--features svt_av1" build-skit +# HW codecs: vulkan_video (H.264 Vulkan Video), vaapi (AV1 VA-API), nvcodec (AV1 NVENC/NVDEC) +# e.g.: just extra_features="--features vulkan_video,nvcodec" skit extra_features := "" # sherpa-onnx version for Kokoro TTS plugin (must match sherpa-rs version) From fa140acd7e043e4092fe6d8324399853e01251ca Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 17:12:52 +0000 Subject: [PATCH 11/69] docs(samples): add HW video codec sample pipelines Add oneshot and dynamic (MoQ) sample pipelines for each HW video codec backend: - Vulkan Video H.264: video_vulkan_video_h264_colorbars (oneshot + MoQ) - VA-API AV1: video_vaapi_av1_colorbars (oneshot + MoQ) - NVENC AV1: video_nv_av1_colorbars (oneshot + MoQ) Each oneshot pipeline generates SMPTE color bars, HW-encodes, muxes into a container (MP4 for H.264, WebM for AV1), and outputs via HTTP. Each dynamic pipeline generates color bars, HW-encodes, and streams via MoQ for live playback in the browser. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- .../dynamic/video_moq_nv_av1_colorbars.yml | 47 +++++++++++++++ .../dynamic/video_moq_vaapi_av1_colorbars.yml | 46 +++++++++++++++ .../video_moq_vulkan_video_h264_colorbars.yml | 46 +++++++++++++++ .../oneshot/video_nv_av1_colorbars.yml | 58 +++++++++++++++++++ .../oneshot/video_vaapi_av1_colorbars.yml | 58 +++++++++++++++++++ .../video_vulkan_video_h264_colorbars.yml | 53 +++++++++++++++++ 6 files changed, 308 insertions(+) create mode 100644 samples/pipelines/dynamic/video_moq_nv_av1_colorbars.yml create mode 100644 samples/pipelines/dynamic/video_moq_vaapi_av1_colorbars.yml create mode 100644 samples/pipelines/dynamic/video_moq_vulkan_video_h264_colorbars.yml create mode 100644 samples/pipelines/oneshot/video_nv_av1_colorbars.yml create mode 100644 samples/pipelines/oneshot/video_vaapi_av1_colorbars.yml create mode 100644 samples/pipelines/oneshot/video_vulkan_video_h264_colorbars.yml diff --git a/samples/pipelines/dynamic/video_moq_nv_av1_colorbars.yml b/samples/pipelines/dynamic/video_moq_nv_av1_colorbars.yml new file mode 100644 index 00000000..fb6572e8 --- /dev/null +++ b/samples/pipelines/dynamic/video_moq_nv_av1_colorbars.yml @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Streams SMPTE color bars encoded with NVIDIA NVENC AV1 (GPU-accelerated) +# over MoQ. +# +# Requires: skit built with --features nvcodec +# NVIDIA GPU with NVENC AV1 support (Ada Lovelace / RTX 40+) +# System packages: nvidia-cuda-toolkit, libclang-dev + +name: NVENC AV1 Color Bars (MoQ Stream) +description: Continuously generates SMPTE color bars and streams via MoQ using NVIDIA NVENC AV1 HW encoder +mode: dynamic +client: + gateway_path: /moq/video + watch: + broadcast: output + audio: false + video: true + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + pixel_format: nv12 + draw_time: true + + nv_av1_encoder: + kind: video::nv::av1_encoder + params: + bitrate: 2000000 + framerate: 30 + needs: colorbars + + moq_peer: + kind: transport::moq::peer + params: + gateway_path: /moq/video + output_broadcast: output + allow_reconnect: true + video_codec: av1 + needs: + in: nv_av1_encoder diff --git a/samples/pipelines/dynamic/video_moq_vaapi_av1_colorbars.yml b/samples/pipelines/dynamic/video_moq_vaapi_av1_colorbars.yml new file mode 100644 index 00000000..112f2345 --- /dev/null +++ b/samples/pipelines/dynamic/video_moq_vaapi_av1_colorbars.yml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Streams SMPTE color bars encoded with VA-API AV1 (GPU-accelerated) over MoQ. +# +# Requires: skit built with --features vaapi +# VA-API capable GPU with AV1 encode support (Intel Arc+, AMD) +# System packages: libva-dev, libgbm-dev + +name: VA-API AV1 Color Bars (MoQ Stream) +description: Continuously generates SMPTE color bars and streams via MoQ using VA-API AV1 HW encoder +mode: dynamic +client: + gateway_path: /moq/video + watch: + broadcast: output + audio: false + video: true + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + pixel_format: nv12 + draw_time: true + + vaapi_av1_encoder: + kind: video::vaapi::av1_encoder + params: + quality: 128 + framerate: 30 + needs: colorbars + + moq_peer: + kind: transport::moq::peer + params: + gateway_path: /moq/video + output_broadcast: output + allow_reconnect: true + video_codec: av1 + needs: + in: vaapi_av1_encoder diff --git a/samples/pipelines/dynamic/video_moq_vulkan_video_h264_colorbars.yml b/samples/pipelines/dynamic/video_moq_vulkan_video_h264_colorbars.yml new file mode 100644 index 00000000..be381fdc --- /dev/null +++ b/samples/pipelines/dynamic/video_moq_vulkan_video_h264_colorbars.yml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Streams SMPTE color bars encoded with Vulkan Video H.264 (GPU-accelerated) +# over MoQ. +# +# Requires: skit built with --features vulkan_video +# Vulkan-capable GPU with H.264 encode support + +name: Vulkan Video H.264 Color Bars (MoQ Stream) +description: Continuously generates SMPTE color bars and streams via MoQ using Vulkan Video H.264 HW encoder +mode: dynamic +client: + gateway_path: /moq/video + watch: + broadcast: output + audio: false + video: true + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + pixel_format: nv12 + draw_time: true + + vk_h264_encoder: + kind: video::vulkan_video::h264_encoder + params: + bitrate: 2000000 + framerate: 30 + needs: colorbars + + moq_peer: + kind: transport::moq::peer + params: + gateway_path: /moq/video + output_broadcast: output + allow_reconnect: true + video_codec: h264 + needs: + in: vk_h264_encoder diff --git a/samples/pipelines/oneshot/video_nv_av1_colorbars.yml b/samples/pipelines/oneshot/video_nv_av1_colorbars.yml new file mode 100644 index 00000000..dddddc21 --- /dev/null +++ b/samples/pipelines/oneshot/video_nv_av1_colorbars.yml @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Demonstrates the NVIDIA NVENC AV1 HW encoder: +# Generates SMPTE color bars (NV12), encodes to AV1 via NVENC +# (GPU-accelerated), muxes into a WebM container, and writes the result +# to HTTP output. +# +# Requires: skit built with --features nvcodec +# NVIDIA GPU with NVENC AV1 support (Ada Lovelace / RTX 40+) +# System packages: nvidia-cuda-toolkit, libclang-dev + +name: NVENC AV1 Encode (WebM Oneshot) +description: Generates color bars, encodes to AV1 using NVIDIA NVENC HW encoder, and muxes into WebM (30 seconds) +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + frame_count: 900 # 30 seconds at 30fps + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + nv_av1_encoder: + kind: video::nv::av1_encoder + params: + bitrate: 2000000 + framerate: 30 + needs: colorbars + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 1280 + video_height: 720 + streaming_mode: live + needs: nv_av1_encoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="av1"' + needs: pacer diff --git a/samples/pipelines/oneshot/video_vaapi_av1_colorbars.yml b/samples/pipelines/oneshot/video_vaapi_av1_colorbars.yml new file mode 100644 index 00000000..9ac5d81c --- /dev/null +++ b/samples/pipelines/oneshot/video_vaapi_av1_colorbars.yml @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Demonstrates the VA-API AV1 HW encoder: +# Generates SMPTE color bars (NV12), encodes to AV1 via VA-API +# (GPU-accelerated), muxes into a WebM container, and writes the result +# to HTTP output. +# +# Requires: skit built with --features vaapi +# VA-API capable GPU with AV1 encode support (Intel Arc+, AMD) +# System packages: libva-dev, libgbm-dev + +name: VA-API AV1 Encode (WebM Oneshot) +description: Generates color bars, encodes to AV1 using VA-API HW encoder, and muxes into WebM (30 seconds) +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + frame_count: 900 # 30 seconds at 30fps + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + vaapi_av1_encoder: + kind: video::vaapi::av1_encoder + params: + quality: 128 + framerate: 30 + needs: colorbars + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 1280 + video_height: 720 + streaming_mode: live + needs: vaapi_av1_encoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="av1"' + needs: pacer diff --git a/samples/pipelines/oneshot/video_vulkan_video_h264_colorbars.yml b/samples/pipelines/oneshot/video_vulkan_video_h264_colorbars.yml new file mode 100644 index 00000000..acbe95ba --- /dev/null +++ b/samples/pipelines/oneshot/video_vulkan_video_h264_colorbars.yml @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Demonstrates the Vulkan Video H.264 HW encoder: +# Generates SMPTE color bars (NV12), encodes to H.264 via Vulkan Video +# (GPU-accelerated), muxes into an MP4 container, and writes the result +# to HTTP output. +# +# Requires: skit built with --features vulkan_video +# Vulkan-capable GPU with H.264 encode support + +name: Vulkan Video H.264 Encode (MP4 Oneshot) +description: Generates color bars, encodes to H.264 using Vulkan Video HW encoder, and muxes into MP4 (30 seconds) +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + frame_count: 900 # 30 seconds at 30fps + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + vk_h264_encoder: + kind: video::vulkan_video::h264_encoder + params: + bitrate: 2000000 + framerate: 30 + needs: colorbars + + mp4_muxer: + kind: containers::mp4::muxer + params: + mode: stream + video_width: 1280 + video_height: 720 + needs: vk_h264_encoder + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/mp4; codecs="avc1.42c01f"' + needs: mp4_muxer From 7f8120a8adff0f7fc985d77781c8d69ed3f431b5 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 17:59:17 +0000 Subject: [PATCH 12/69] fix(nodes): revert get_plane_offset to computed fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_plane_offset() is private in cros-codecs 0.0.6. Fall back to computing the UV plane offset from pitch × coded_height, which is correct for linear NV12 allocations used by VA-API encode surfaces. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index adc6129c..93069bd7 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -950,10 +950,13 @@ impl StandardVideoEncoder for VaapiAv1Encoder { let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); - // Use actual GBM plane offsets instead of computing them manually. - // Different drivers may place the UV plane at an offset that differs - // from `y_stride * coded_height` (e.g. with extra padding rows). - let offsets = gbm_frame.get_plane_offset(); + // Ideally we'd use `gbm_frame.get_plane_offset()` to get the real UV + // plane offset from the GBM allocator, but that method is private in + // cros-codecs 0.0.6. Fall back to computing it from pitch × coded_height, + // which is correct for linear (non-tiled) NV12 allocations — the common + // case for VA-API encode surfaces. + let y_stride = pitches.first().copied().unwrap_or(self.coded_width as usize); + let uv_offset = y_stride * self.coded_height as usize; let frame_layout = FrameLayout { format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR @@ -961,15 +964,12 @@ impl StandardVideoEncoder for VaapiAv1Encoder { planes: vec![ PlaneLayout { buffer_index: 0, - offset: offsets.first().copied().unwrap_or(0), - stride: pitches.first().copied().unwrap_or(self.coded_width as usize), + offset: 0, + stride: y_stride, }, PlaneLayout { buffer_index: 0, - offset: offsets.get(1).copied().unwrap_or( - pitches.first().copied().unwrap_or(self.coded_width as usize) - * self.coded_height as usize, - ), + offset: uv_offset, stride: pitches.get(1).copied().unwrap_or(self.coded_width as usize), }, ], From 49d7dae49a8cd7bcfc9b61725dc33794f10b8e5e Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 17:59:27 +0000 Subject: [PATCH 13/69] style: format vaapi_av1.rs Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 93069bd7..4f67d924 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -962,11 +962,7 @@ impl StandardVideoEncoder for VaapiAv1Encoder { format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR size: CrosResolution { width: self.coded_width, height: self.coded_height }, planes: vec![ - PlaneLayout { - buffer_index: 0, - offset: 0, - stride: y_stride, - }, + PlaneLayout { buffer_index: 0, offset: 0, stride: y_stride }, PlaneLayout { buffer_index: 0, offset: uv_offset, From 1dab1455dd11dd13db2ed70e452fb9987d4434c0 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 18:03:05 +0000 Subject: [PATCH 14/69] feat(nodes): add VA-API H.264 encoder and decoder nodes Add vaapi_h264 module with VaapiH264EncoderNode and VaapiH264DecoderNode using cros-codecs StatelessEncoder/StatelessDecoder for H.264 via VA-API. - Encoder: CQP rate control, Main profile, macroblock-aligned coding - Decoder: stateless H.264 decode with format-change handling - Reuses shared helpers from vaapi_av1 (GBM/NV12 I/O, device detection) - Registration: video::vaapi::h264_encoder, video::vaapi::h264_decoder - Sample pipelines: oneshot MP4 + dynamic MoQ for VA-API H.264 Supported on Intel (Sandy Bridge+), AMD, and NVIDIA (decode only). Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/mod.rs | 6 + crates/nodes/src/video/vaapi_av1.rs | 12 +- crates/nodes/src/video/vaapi_h264.rs | 1062 +++++++++++++++++ .../video_moq_vaapi_h264_colorbars.yml | 46 + .../oneshot/video_vaapi_h264_colorbars.yml | 54 + 5 files changed, 1174 insertions(+), 6 deletions(-) create mode 100644 crates/nodes/src/video/vaapi_h264.rs create mode 100644 samples/pipelines/dynamic/video_moq_vaapi_h264_colorbars.yml create mode 100644 samples/pipelines/oneshot/video_vaapi_h264_colorbars.yml diff --git a/crates/nodes/src/video/mod.rs b/crates/nodes/src/video/mod.rs index 43f51323..f3b2b6f3 100644 --- a/crates/nodes/src/video/mod.rs +++ b/crates/nodes/src/video/mod.rs @@ -146,6 +146,9 @@ pub mod vulkan_video; #[cfg(feature = "vaapi")] pub mod vaapi_av1; +#[cfg(feature = "vaapi")] +pub mod vaapi_h264; + #[cfg(feature = "nvcodec")] pub mod nv_av1; @@ -643,6 +646,9 @@ pub fn register_video_nodes(registry: &mut NodeRegistry, constraints: &GlobalNod #[cfg(feature = "vaapi")] vaapi_av1::register_vaapi_av1_nodes(registry); + #[cfg(feature = "vaapi")] + vaapi_h264::register_vaapi_h264_nodes(registry); + #[cfg(feature = "nvcodec")] nv_av1::register_nv_av1_nodes(registry); } diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 4f67d924..f69af6e9 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -101,12 +101,12 @@ const DEFAULT_FRAMERATE: u32 = 30; // --------------------------------------------------------------------------- /// NV12 fourcc code for GBM/VA-API surfaces. -fn nv12_fourcc() -> CrosFourcc { +pub(super) fn nv12_fourcc() -> CrosFourcc { CrosFourcc::from(b"NV12") } /// Align `value` up to the next multiple of `alignment`. -fn align_up_u32(value: u32, alignment: u32) -> u32 { +pub(super) fn align_up_u32(value: u32, alignment: u32) -> u32 { debug_assert!(alignment > 0); value.div_ceil(alignment) * alignment } @@ -134,7 +134,7 @@ fn detect_render_device() -> Option { } /// Resolve the render device path from config, auto-detection, or default. -fn resolve_render_device(configured: Option<&String>) -> String { +pub(super) fn resolve_render_device(configured: Option<&String>) -> String { if let Some(path) = configured { return path.clone(); } @@ -152,7 +152,7 @@ fn resolve_render_device(configured: Option<&String>) -> String { } /// Open a VA display and a GBM device on the same render node. -fn open_va_and_gbm( +pub(super) fn open_va_and_gbm( render_device: Option<&String>, ) -> Result<(Rc, Arc, String), String> { let path = resolve_render_device(render_device); @@ -167,7 +167,7 @@ fn open_va_and_gbm( /// for a packed StreamKit [`VideoFrame`]. /// /// Handles stride != width by copying row-by-row. -fn read_nv12_from_mapping( +pub(super) fn read_nv12_from_mapping( mapping: &dyn ReadMapping<'_>, width: u32, height: u32, @@ -226,7 +226,7 @@ fn read_nv12_from_mapping( /// /// If the source is I420, it is converted to NV12 on the fly (U/V planes /// are interleaved into a single UV plane). -fn write_nv12_to_mapping( +pub(super) fn write_nv12_to_mapping( mapping: &dyn WriteMapping<'_>, frame: &VideoFrame, plane_pitches: &[usize], diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs new file mode 100644 index 00000000..f4373538 --- /dev/null +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -0,0 +1,1062 @@ +// SPDX-FileCopyrightText: © 2025 StreamKit Contributors +// +// SPDX-License-Identifier: MPL-2.0 + +//! VA-API HW-accelerated H.264 encoder and decoder nodes. +//! +//! Uses the [`cros-codecs`](https://crates.io/crates/cros-codecs) crate which +//! provides high-level VA-API H.264 codec abstractions on Linux. The cros-codecs +//! `StatelessDecoder` and `StatelessEncoder` handle all H.264 bitstream parsing +//! and VA-API parameter buffer construction internally — this module manages +//! frame I/O and integrates with StreamKit's pipeline architecture. +//! +//! # Nodes +//! +//! - [`VaapiH264DecoderNode`] — decodes H.264 NAL packets to NV12 [`VideoFrame`]s +//! - [`VaapiH264EncoderNode`] — encodes NV12/I420 [`VideoFrame`]s to H.264 packets +//! +//! Both perform runtime capability detection: if no VA-API device is found (or +//! H.264 is not supported), the codec task returns an error so the pipeline can +//! fall back to a CPU codec (OpenH264). +//! +//! # Feature gate +//! +//! Requires `vaapi` Cargo feature and `libva-dev` + `libgbm-dev` system packages. +//! +//! # Platform support +//! +//! - **Intel**: H.264 encode + decode on all modern Intel GPUs (Sandy Bridge+). +//! - **AMD**: H.264 encode + decode via Mesa RadeonSI VA-API. +//! - **NVIDIA**: Decode only via community `nvidia-vaapi-driver` (no VA-API encoding). + +use std::rc::Rc; +use std::sync::Arc; +use std::time::Instant; + +use async_trait::async_trait; +use bytes::Bytes; +use opentelemetry::global; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use streamkit_core::stats::NodeStatsTracker; +use streamkit_core::types::{ + EncodedVideoFormat, Packet, PacketMetadata, PacketType, PixelFormat, RawVideoFormat, + VideoCodec, VideoFrame, +}; +use streamkit_core::{ + config_helpers, get_codec_channel_capacity, packet_helpers, state_helpers, InputPin, + NodeContext, NodeRegistry, OutputPin, PinCardinality, ProcessorNode, StreamKitError, +}; +use tokio::sync::mpsc; + +// cros-codecs high-level APIs. +use cros_codecs::backend::vaapi::decoder::VaapiBackend as VaapiDecBackend; +use cros_codecs::codec::h264::parser::Level as H264Level; +use cros_codecs::codec::h264::parser::Profile as H264Profile; +use cros_codecs::decoder::stateless::h264::H264; +use cros_codecs::decoder::stateless::{DecodeError, StatelessDecoder, StatelessVideoDecoder}; +use cros_codecs::decoder::{BlockingMode, DecodedHandle, DecoderEvent}; +use cros_codecs::encoder::h264::EncoderConfig as CrosH264EncoderConfig; +use cros_codecs::encoder::stateless::StatelessEncoder; +use cros_codecs::encoder::{ + FrameMetadata as CrosFrameMetadata, PredictionStructure, RateControl, Tunings, VideoEncoder, +}; +use cros_codecs::libva; +use cros_codecs::video_frame::gbm_video_frame::{ + GbmDevice, GbmExternalBufferDescriptor, GbmUsage, GbmVideoFrame, +}; +use cros_codecs::video_frame::{ReadMapping, VideoFrame as CrosVideoFrame, WriteMapping}; +use cros_codecs::{Fourcc as CrosFourcc, FrameLayout, PlaneLayout, Resolution as CrosResolution}; + +use super::encoder_trait::{self, EncodedPacket, EncoderNodeRunner, StandardVideoEncoder}; +use super::HwAccelMode; +use super::H264_CONTENT_TYPE; + +// Re-use helpers from the VA-API AV1 module — they are codec-agnostic NV12 +// I/O routines (GBM mapping, render-device detection, etc.). +use super::vaapi_av1::{ + align_up_u32, nv12_fourcc, open_va_and_gbm, read_nv12_from_mapping, write_nv12_to_mapping, +}; + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +/// H.264 macroblock size — coded resolution must be aligned to this. +const H264_MB_SIZE: u32 = 16; + +/// Maximum number of consecutive retries when the decoder returns +/// `CheckEvents` or `NotEnoughOutputBuffers` without making progress. +const MAX_EAGAIN_EMPTY_RETRIES: u32 = 1000; + +/// After this many retries, switch from `thread::yield_now()` to +/// `thread::sleep(1ms)` to avoid a tight spin-loop. +const EAGAIN_YIELD_THRESHOLD: u32 = 10; + +/// Default constant-quality parameter for H.264 (0–51 QP scale). +const DEFAULT_QUALITY: u32 = 26; + +/// Default framerate for rate-control hints. +const DEFAULT_FRAMERATE: u32 = 30; + +// --------------------------------------------------------------------------- +// Decoder +// --------------------------------------------------------------------------- + +/// Configuration for the VA-API H.264 hardware decoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct VaapiH264DecoderConfig { + /// Path to the DRM render device (e.g. `/dev/dri/renderD128`). + /// When `None`, auto-detects the first VA-API capable device. + pub render_device: Option, + + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, +} + +impl Default for VaapiH264DecoderConfig { + fn default() -> Self { + Self { + render_device: None, + hw_accel: HwAccelMode::Auto, + } + } +} + +pub struct VaapiH264DecoderNode { + config: VaapiH264DecoderConfig, +} + +impl VaapiH264DecoderNode { + #[allow(clippy::missing_errors_doc)] + pub fn new(config: VaapiH264DecoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "VaapiH264DecoderNode only supports hardware decoding; \ + use video::h264::decoder for CPU decode" + .into(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for VaapiH264DecoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::H264, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + })], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + async fn run(self: Box, mut context: NodeContext) -> Result<(), StreamKitError> { + let node_name = context.output_sender.node_name().to_string(); + state_helpers::emit_initializing(&context.state_tx, &node_name); + + tracing::info!("VaapiH264DecoderNode starting"); + let mut input_rx = context.take_input("in")?; + + let meter = global::meter("skit_nodes"); + let packets_processed_counter = + meter.u64_counter("vaapi_h264_decoder_packets_processed").build(); + let decode_duration_histogram = meter + .f64_histogram("vaapi_h264_decode_duration") + .with_boundaries(streamkit_core::metrics::HISTOGRAM_BOUNDARIES_CODEC_PACKET.to_vec()) + .build(); + + let (decode_tx, decode_rx) = + mpsc::channel::<(Bytes, Option)>(get_codec_channel_capacity()); + let (result_tx, mut result_rx) = + mpsc::channel::>(get_codec_channel_capacity()); + + let render_device = self.config.render_device.clone(); + let decode_task = tokio::task::spawn_blocking(move || { + vaapi_h264_decode_loop( + render_device.as_ref(), + decode_rx, + &result_tx, + &decode_duration_histogram, + ); + }); + + state_helpers::emit_running(&context.state_tx, &node_name); + + let mut stats_tracker = NodeStatsTracker::new(node_name.clone(), context.stats_tx.clone()); + let batch_size = context.batch_size; + + let decode_tx_clone = decode_tx.clone(); + let mut input_task = tokio::spawn(async move { + loop { + let Some(first_packet) = input_rx.recv().await else { + break; + }; + + let packet_batch = + packet_helpers::batch_packets_greedy(first_packet, &mut input_rx, batch_size); + + for packet in packet_batch { + if let Packet::Binary { data, metadata, .. } = packet { + if decode_tx_clone.send((data, metadata)).await.is_err() { + tracing::error!( + "VaapiH264DecoderNode decode task has shut down unexpectedly" + ); + return; + } + } + } + } + tracing::info!("VaapiH264DecoderNode input stream closed"); + }); + + crate::codec_utils::codec_forward_loop( + &mut context, + &mut result_rx, + &mut input_task, + decode_task, + decode_tx, + &packets_processed_counter, + &mut stats_tracker, + Packet::Video, + "VaapiH264DecoderNode", + ) + .await; + + state_helpers::emit_stopped(&context.state_tx, &node_name, "input_closed"); + tracing::info!("VaapiH264DecoderNode finished"); + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// Decoder — blocking decode loop +// --------------------------------------------------------------------------- + +/// Blocking decode loop running inside `spawn_blocking`. +/// +/// Creates the VA-API display, GBM device, and cros-codecs `StatelessDecoder`, +/// then processes input packets until the channel is closed. +fn vaapi_h264_decode_loop( + render_device: Option<&String>, + mut decode_rx: mpsc::Receiver<(Bytes, Option)>, + result_tx: &mpsc::Sender>, + duration_histogram: &opentelemetry::metrics::Histogram, +) { + // ── Open GBM device + VA display ────────────────────────────────── + let (display, gbm, path) = match open_va_and_gbm(render_device) { + Ok(v) => v, + Err(e) => { + let _ = result_tx.blocking_send(Err(e)); + return; + } + }; + tracing::info!(device = %path, "VA-API H.264 decoder opened display"); + + // ── Create stateless decoder ───────────────────────────────────── + let mut decoder = match StatelessDecoder::>::new_vaapi( + display, + BlockingMode::Blocking, + ) { + Ok(d) => d, + Err(e) => { + let _ = + result_tx.blocking_send(Err(format!("failed to create VA-API H.264 decoder: {e}"))); + return; + } + }; + + // Stream resolution — updated on FormatChanged events. + let mut coded_width: u32 = 0; + let mut coded_height: u32 = 0; + + while let Some((data, metadata)) = decode_rx.blocking_recv() { + if result_tx.is_closed() { + return; + } + + let decode_start = Instant::now(); + let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(0); + + // Feed bitstream to the decoder. + let mut offset = 0usize; + let bitstream = data.as_ref(); + let mut eagain_empty_retries: u32 = 0; + + while offset < bitstream.len() { + let gbm_ref = Arc::clone(&gbm); + let cw = coded_width; + let ch = coded_height; + let mut alloc_cb = move || { + gbm_ref + .clone() + .new_frame( + nv12_fourcc(), + CrosResolution { width: cw, height: ch }, + CrosResolution { width: cw, height: ch }, + GbmUsage::Decode, + ) + .ok() + }; + + let mut made_progress = false; + + match decoder.decode(timestamp, &bitstream[offset..], &mut alloc_cb) { + Ok(bytes_consumed) => { + offset += bytes_consumed; + made_progress = true; + } + Err(DecodeError::CheckEvents | DecodeError::NotEnoughOutputBuffers(_)) => { + // Process pending events / drain ready frames, then retry. + } + Err(e) => { + tracing::error!(error = %e, "VA-API H.264 decode error"); + let _ = + result_tx.blocking_send(Err(format!("VA-API H.264 decode error: {e}"))); + break; + } + } + + // Process all pending events (format changes + ready frames). + let (should_exit, had_events) = drain_decoder_events( + &mut decoder, + result_tx, + metadata.as_ref(), + &mut coded_width, + &mut coded_height, + ); + if should_exit { + return; + } + + if made_progress || had_events { + eagain_empty_retries = 0; + } else { + eagain_empty_retries += 1; + if eagain_empty_retries > MAX_EAGAIN_EMPTY_RETRIES { + tracing::error!( + "VA-API H.264 decoder stuck: no progress after {MAX_EAGAIN_EMPTY_RETRIES} retries" + ); + let _ = result_tx.blocking_send(Err( + "VA-API H.264 decoder stuck in CheckEvents/NotEnoughOutputBuffers loop" + .to_string(), + )); + break; + } + // Progressive backoff to avoid a tight spin-loop. + if eagain_empty_retries <= EAGAIN_YIELD_THRESHOLD { + std::thread::yield_now(); + } else { + std::thread::sleep(std::time::Duration::from_millis(1)); + } + } + } + + duration_histogram.record(decode_start.elapsed().as_secs_f64(), &[]); + } + + // Flush remaining frames from the decoder. + if result_tx.is_closed() { + return; + } + if let Err(e) = decoder.flush() { + tracing::warn!(error = %e, "VA-API H.264 decoder flush failed"); + } + drain_decoder_events( + &mut decoder, + result_tx, + None, + &mut coded_width, + &mut coded_height, + ); +} + +/// Drain all pending events from the decoder. +/// +/// Returns `(should_exit, had_events)`: +/// - `should_exit`: the result channel is closed and the caller should return. +/// - `had_events`: at least one event (format change or frame) was processed. +fn drain_decoder_events( + decoder: &mut StatelessDecoder>, + result_tx: &mpsc::Sender>, + metadata: Option<&PacketMetadata>, + coded_width: &mut u32, + coded_height: &mut u32, +) -> (bool, bool) { + let mut had_events = false; + while let Some(event) = decoder.next_event() { + had_events = true; + match event { + DecoderEvent::FormatChanged => { + if let Some(info) = decoder.stream_info() { + let dw = info.display_resolution.width; + let dh = info.display_resolution.height; + *coded_width = info.coded_resolution.width; + *coded_height = info.coded_resolution.height; + tracing::info!( + display_width = dw, + display_height = dh, + coded_width = *coded_width, + coded_height = *coded_height, + "VA-API H.264 decoder stream format changed" + ); + } + } + DecoderEvent::FrameReady(handle) => { + if let Err(e) = handle.sync() { + tracing::error!(error = %e, "VA-API H.264 frame sync failed"); + continue; + } + + let display_res = handle.display_resolution(); + let frame_w = display_res.width; + let frame_h = display_res.height; + + let gbm_frame = handle.video_frame(); + let pitches = gbm_frame.get_plane_pitch(); + + // Extract NV12 data while the mapping is alive. + let nv12_data = { + let mapping = match gbm_frame.map() { + Ok(m) => m, + Err(e) => { + tracing::error!(error = %e, "failed to map decoded GBM frame"); + continue; + } + }; + read_nv12_from_mapping(mapping.as_ref(), frame_w, frame_h, &pitches) + }; + + match VideoFrame::with_metadata( + frame_w, + frame_h, + PixelFormat::Nv12, + nv12_data, + metadata.cloned(), + ) { + Ok(frame) => { + if result_tx.blocking_send(Ok(frame)).is_err() { + return (true, had_events); + } + } + Err(e) => { + tracing::error!( + error = %e, + "failed to construct VideoFrame from decoded data" + ); + } + } + } + } + } + (false, had_events) +} + +// --------------------------------------------------------------------------- +// Encoder +// --------------------------------------------------------------------------- + +/// Configuration for the VA-API H.264 hardware encoder node. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default, deny_unknown_fields)] +pub struct VaapiH264EncoderConfig { + /// Path to the DRM render device (e.g. `/dev/dri/renderD128`). + /// When `None`, auto-detects the first VA-API capable device. + pub render_device: Option, + + /// Constant quality parameter (QP). Lower values produce higher quality + /// at the cost of larger bitstream. H.264 QP range is 0–51, default 26. + pub quality: u32, + + /// Target framerate in frames per second (used for rate control hints). + pub framerate: u32, + + /// Use low-power encoding mode if the driver supports it. + /// Low-power mode uses the GPU's fixed-function encoder (if available) + /// rather than shader-based encoding, typically offering lower latency + /// at reduced quality flexibility. + pub low_power: bool, + + /// Hardware acceleration mode. + pub hw_accel: HwAccelMode, +} + +impl Default for VaapiH264EncoderConfig { + fn default() -> Self { + Self { + render_device: None, + quality: DEFAULT_QUALITY, + framerate: DEFAULT_FRAMERATE, + low_power: false, + hw_accel: HwAccelMode::Auto, + } + } +} + +pub struct VaapiH264EncoderNode { + config: VaapiH264EncoderConfig, +} + +impl VaapiH264EncoderNode { + #[allow(clippy::missing_errors_doc)] + pub fn new(config: VaapiH264EncoderConfig) -> Result { + if matches!(config.hw_accel, HwAccelMode::ForceCpu) { + return Err(StreamKitError::Configuration( + "VaapiH264EncoderNode only supports hardware encoding; \ + use video::h264::encoder for CPU encode" + .into(), + )); + } + Ok(Self { config }) + } +} + +#[async_trait] +impl ProcessorNode for VaapiH264EncoderNode { + fn input_pins(&self) -> Vec { + vec![InputPin { + name: "in".to_string(), + accepts_types: vec![ + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::I420, + }), + PacketType::RawVideo(RawVideoFormat { + width: None, + height: None, + pixel_format: PixelFormat::Nv12, + }), + ], + cardinality: PinCardinality::One, + }] + } + + fn output_pins(&self) -> Vec { + vec![OutputPin { + name: "out".to_string(), + produces_type: PacketType::EncodedVideo(EncodedVideoFormat { + codec: VideoCodec::H264, + bitstream_format: None, + codec_private: None, + profile: None, + level: None, + }), + cardinality: PinCardinality::Broadcast, + }] + } + + fn content_type(&self) -> Option { + Some(H264_CONTENT_TYPE.to_string()) + } + + async fn run(self: Box, context: NodeContext) -> Result<(), StreamKitError> { + encoder_trait::run_encoder(*self, context).await + } +} + +impl EncoderNodeRunner for VaapiH264EncoderNode { + const CONTENT_TYPE: &'static str = H264_CONTENT_TYPE; + const NODE_LABEL: &'static str = "VaapiH264EncoderNode"; + const PACKETS_COUNTER_NAME: &'static str = "vaapi_h264_encoder_packets_processed"; + const DURATION_HISTOGRAM_NAME: &'static str = "vaapi_h264_encode_duration"; + + fn spawn_codec_task( + self, + encode_rx: mpsc::Receiver<(VideoFrame, Option)>, + result_tx: mpsc::Sender>, + duration_histogram: opentelemetry::metrics::Histogram, + ) -> tokio::task::JoinHandle<()> { + encoder_trait::spawn_standard_encode_task::( + self.config, + encode_rx, + result_tx, + duration_histogram, + ) + } +} + +// --------------------------------------------------------------------------- +// Encoder — internal codec wrapper +// --------------------------------------------------------------------------- + +/// Type alias for the full VA-API H.264 encoder with GBM-backed frames. +type CrosVaapiH264Encoder = StatelessEncoder< + cros_codecs::encoder::h264::H264, + GbmVideoFrame, + cros_codecs::backend::vaapi::encoder::VaapiBackend< + GbmExternalBufferDescriptor, + libva::Surface, + >, +>; + +/// Internal encoder state wrapping the cros-codecs `StatelessEncoder`. +/// +/// `!Send` due to internal `Rc` — lives entirely inside +/// a `spawn_blocking` thread. +struct VaapiH264Encoder { + encoder: CrosVaapiH264Encoder, + gbm: Arc, + width: u32, + height: u32, + coded_width: u32, + coded_height: u32, + frame_count: u64, +} + +impl StandardVideoEncoder for VaapiH264Encoder { + type Config = VaapiH264EncoderConfig; + const CODEC_NAME: &'static str = "VA-API H.264"; + + fn new_encoder(width: u32, height: u32, config: &Self::Config) -> Result { + let (display, gbm, path) = open_va_and_gbm(config.render_device.as_ref())?; + tracing::info!(device = %path, width, height, "VA-API H.264 encoder opening"); + + let coded_width = align_up_u32(width, H264_MB_SIZE); + let coded_height = align_up_u32(height, H264_MB_SIZE); + + let cros_config = CrosH264EncoderConfig { + resolution: CrosResolution { + width: coded_width, + height: coded_height, + }, + profile: H264Profile::Main, + level: H264Level::L4, + pred_structure: PredictionStructure::LowDelay { limit: 1024 }, + initial_tunings: Tunings { + rate_control: RateControl::ConstantQuality(config.quality), + framerate: config.framerate, + min_quality: 0, + max_quality: 51, + }, + }; + + let encoder = CrosVaapiH264Encoder::new_vaapi( + display, + cros_config, + nv12_fourcc(), + CrosResolution { + width: coded_width, + height: coded_height, + }, + config.low_power, + BlockingMode::Blocking, + ) + .map_err(|e| format!("failed to create VA-API H.264 encoder: {e}"))?; + + tracing::info!( + device = %path, + width, + height, + coded_width, + coded_height, + quality = config.quality, + "VA-API H.264 encoder created" + ); + + Ok(Self { + encoder, + gbm, + width, + height, + coded_width, + coded_height, + frame_count: 0, + }) + } + + fn encode( + &mut self, + frame: &VideoFrame, + metadata: Option, + ) -> Result, String> { + if frame.pixel_format == PixelFormat::Rgba8 { + return Err( + "VA-API H.264 encoder requires NV12 or I420 input; \ + insert a video::pixel_convert node upstream" + .into(), + ); + } + + // Create a GBM frame and upload the raw video data. + let mut gbm_frame = Arc::clone(&self.gbm) + .new_frame( + nv12_fourcc(), + CrosResolution { + width: self.width, + height: self.height, + }, + CrosResolution { + width: self.coded_width, + height: self.coded_height, + }, + GbmUsage::Encode, + ) + .map_err(|e| format!("failed to allocate GBM frame for encoding: {e}"))?; + + // Write frame data into the GBM buffer. + let pitches = gbm_frame.get_plane_pitch(); + { + let mapping = gbm_frame + .map_mut() + .map_err(|e| format!("failed to map GBM frame for writing: {e}"))?; + write_nv12_to_mapping(mapping.as_ref(), frame, &pitches)?; + } + + let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); + let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); + + // Compute UV plane offset from pitch × coded_height (same approach as + // the AV1 encoder — get_plane_offset() is private in cros-codecs 0.0.6). + let y_stride = pitches.first().copied().unwrap_or(self.coded_width as usize); + let uv_offset = y_stride * self.coded_height as usize; + + let frame_layout = FrameLayout { + format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR + size: CrosResolution { + width: self.coded_width, + height: self.coded_height, + }, + planes: vec![ + PlaneLayout { + buffer_index: 0, + offset: 0, + stride: y_stride, + }, + PlaneLayout { + buffer_index: 0, + offset: uv_offset, + stride: pitches.get(1).copied().unwrap_or(self.coded_width as usize), + }, + ], + }; + + let cros_meta = + CrosFrameMetadata { timestamp, layout: frame_layout, force_keyframe: is_keyframe }; + + self.encoder + .encode(cros_meta, gbm_frame) + .map_err(|e| format!("VA-API H.264 encode error: {e}"))?; + + self.frame_count += 1; + + // Poll for all available encoded output. + let mut packets = Vec::new(); + loop { + match self.encoder.poll() { + Ok(Some(coded)) => { + packets.push(EncodedPacket { + data: Bytes::from(coded.bitstream), + metadata: metadata.clone(), + }); + } + Ok(None) => break, + Err(e) => return Err(format!("VA-API H.264 encoder poll error: {e}")), + } + } + + Ok(packets) + } + + fn flush_encoder(&mut self) -> Result, String> { + self.encoder + .drain() + .map_err(|e| format!("VA-API H.264 encoder drain error: {e}"))?; + + let mut packets = Vec::new(); + loop { + match self.encoder.poll() { + Ok(Some(coded)) => { + packets + .push(EncodedPacket { data: Bytes::from(coded.bitstream), metadata: None }); + } + Ok(None) => break, + Err(e) => return Err(format!("VA-API H.264 encoder poll error: {e}")), + } + } + + Ok(packets) + } + + fn flush_on_dimension_change() -> bool { + true + } +} + +// --------------------------------------------------------------------------- +// Registration +// --------------------------------------------------------------------------- + +use schemars::schema_for; +use streamkit_core::registry::StaticPins; + +#[allow(clippy::expect_used, clippy::missing_panics_doc)] +pub fn register_vaapi_h264_nodes(registry: &mut NodeRegistry) { + let default_decoder = VaapiH264DecoderNode::new(VaapiH264DecoderConfig::default()) + .expect("default VA-API H.264 decoder config should be valid"); + registry.register_static_with_description( + "video::vaapi::h264_decoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(VaapiH264DecoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(VaapiH264DecoderConfig)) + .expect("VaapiH264DecoderConfig schema should serialize to JSON"), + StaticPins { + inputs: default_decoder.input_pins(), + outputs: default_decoder.output_pins(), + }, + vec![ + "video".to_string(), + "codecs".to_string(), + "h264".to_string(), + "hw".to_string(), + "vaapi".to_string(), + ], + false, + "Decodes H.264-compressed packets into raw NV12 video frames using VA-API \ + hardware acceleration. Requires a VA-API capable GPU (Intel Sandy Bridge+, \ + AMD, or NVIDIA with nvidia-vaapi-driver).", + ); + + let default_encoder = VaapiH264EncoderNode::new(VaapiH264EncoderConfig::default()) + .expect("default VA-API H.264 encoder config should be valid"); + registry.register_static_with_description( + "video::vaapi::h264_encoder", + |params| { + let config = config_helpers::parse_config_optional(params)?; + Ok(Box::new(VaapiH264EncoderNode::new(config)?)) + }, + serde_json::to_value(schema_for!(VaapiH264EncoderConfig)) + .expect("VaapiH264EncoderConfig schema should serialize to JSON"), + StaticPins { + inputs: default_encoder.input_pins(), + outputs: default_encoder.output_pins(), + }, + vec![ + "video".to_string(), + "codecs".to_string(), + "h264".to_string(), + "hw".to_string(), + "vaapi".to_string(), + ], + false, + "Encodes raw NV12/I420 video frames into H.264-compressed packets using VA-API \ + hardware acceleration. Uses constant-quality (CQP) rate control. Requires a \ + VA-API capable GPU with H.264 encode support (Intel, AMD).", + ); +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::disallowed_macros)] +mod tests { + use super::*; + + // ── Unit tests (no GPU required) ───────────────────────────────── + + #[test] + fn test_force_cpu_rejected_decoder() { + let config = + VaapiH264DecoderConfig { hw_accel: HwAccelMode::ForceCpu, ..Default::default() }; + let result = VaapiH264DecoderNode::new(config); + assert!(result.is_err(), "ForceCpu should be rejected for VA-API H.264 decoder"); + } + + #[test] + fn test_force_cpu_rejected_encoder() { + let config = + VaapiH264EncoderConfig { hw_accel: HwAccelMode::ForceCpu, ..Default::default() }; + let result = VaapiH264EncoderNode::new(config); + assert!(result.is_err(), "ForceCpu should be rejected for VA-API H.264 encoder"); + } + + #[test] + fn test_default_configs() { + let dec = VaapiH264DecoderConfig::default(); + assert!(dec.render_device.is_none()); + assert!(matches!(dec.hw_accel, HwAccelMode::Auto)); + + let enc = VaapiH264EncoderConfig::default(); + assert!(enc.render_device.is_none()); + assert_eq!(enc.quality, DEFAULT_QUALITY); + assert_eq!(enc.framerate, DEFAULT_FRAMERATE); + assert!(!enc.low_power); + assert!(matches!(enc.hw_accel, HwAccelMode::Auto)); + } + + #[test] + fn test_decoder_pins() { + let node = VaapiH264DecoderNode::new(VaapiH264DecoderConfig::default()).unwrap(); + assert_eq!(node.input_pins().len(), 1); + assert_eq!(node.output_pins().len(), 1); + assert_eq!(node.input_pins()[0].name, "in"); + assert_eq!(node.output_pins()[0].name, "out"); + } + + #[test] + fn test_encoder_pins() { + let node = VaapiH264EncoderNode::new(VaapiH264EncoderConfig::default()).unwrap(); + assert_eq!(node.input_pins().len(), 1); + assert_eq!(node.output_pins().len(), 1); + assert_eq!(node.input_pins()[0].name, "in"); + assert_eq!(node.output_pins()[0].name, "out"); + // Encoder should accept both I420 and NV12 inputs. + assert_eq!(node.input_pins()[0].accepts_types.len(), 2); + } + + #[test] + fn test_encoder_content_type() { + let node = VaapiH264EncoderNode::new(VaapiH264EncoderConfig::default()).unwrap(); + assert_eq!(node.content_type(), Some(H264_CONTENT_TYPE.to_string())); + } + + // ── Registration test ──────────────────────────────────────────── + + #[test] + fn test_registration() { + let mut registry = NodeRegistry::new(); + register_vaapi_h264_nodes(&mut registry); + assert!( + registry.create_node("video::vaapi::h264_decoder", None).is_ok(), + "VA-API H.264 decoder should be registered" + ); + assert!( + registry.create_node("video::vaapi::h264_encoder", None).is_ok(), + "VA-API H.264 encoder should be registered" + ); + } + + // ── GPU integration tests ──────────────────────────────────────── + // + // These require a VA-API capable GPU with H.264 support. They are + // compiled with the `vaapi` feature but skip at runtime if no VA-API + // device is available. + + /// Check whether a usable VA-API display can be opened. + fn vaapi_available() -> bool { + use super::super::vaapi_av1::resolve_render_device; + let path = resolve_render_device(None); + libva::Display::open_drm_display(std::path::Path::new(&path)).is_ok() + } + + /// Encoder + Decoder roundtrip: encode 5 NV12 frames, decode them back, + /// verify dimensions and pixel format. + #[tokio::test] + async fn test_vaapi_h264_encode_decode_roundtrip() { + if !vaapi_available() { + eprintln!("SKIP: no VA-API device available"); + return; + } + + use crate::test_utils::{ + assert_state_initializing, assert_state_running, assert_state_stopped, + create_test_context, create_test_video_frame, + }; + use std::borrow::Cow; + use std::collections::HashMap; + + // --- Encode --- + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder_config = VaapiH264EncoderConfig { + render_device: None, + hw_accel: HwAccelMode::Auto, + quality: 40, // fast, lower quality for test speed + framerate: 30, + low_power: false, + }; + let encoder = VaapiH264EncoderNode::new(encoder_config).unwrap(); + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + for index in 0_u64..5 { + let mut frame = create_test_video_frame(64, 64, PixelFormat::Nv12, 16); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(1_000 + 33_333 * index), + duration_us: Some(33_333), + sequence: Some(index), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + } + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty(), "VA-API H.264 encoder produced no packets"); + + // --- Decode --- + let (dec_input_tx, dec_input_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_input_rx); + + let (dec_context, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = VaapiH264DecoderNode::new(VaapiH264DecoderConfig::default()).unwrap(); + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_context).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + for packet in encoded_packets { + if let Packet::Binary { data, metadata, .. } = packet { + dec_input_tx + .send(Packet::Binary { + data, + content_type: Some(Cow::Borrowed(H264_CONTENT_TYPE)), + metadata, + }) + .await + .unwrap(); + } + } + drop(dec_input_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "VA-API H.264 decoder produced no frames"); + + for packet in decoded_packets { + match packet { + Packet::Video(frame) => { + assert_eq!(frame.width, 64); + assert_eq!(frame.height, 64); + assert_eq!(frame.pixel_format, PixelFormat::Nv12); + assert!(!frame.data().is_empty(), "Decoded frame should have data"); + } + _ => panic!("Expected Video packet from VA-API H.264 decoder"), + } + } + } +} diff --git a/samples/pipelines/dynamic/video_moq_vaapi_h264_colorbars.yml b/samples/pipelines/dynamic/video_moq_vaapi_h264_colorbars.yml new file mode 100644 index 00000000..30a10638 --- /dev/null +++ b/samples/pipelines/dynamic/video_moq_vaapi_h264_colorbars.yml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Streams SMPTE color bars encoded with VA-API H.264 (GPU-accelerated) over MoQ. +# +# Requires: skit built with --features vaapi +# VA-API capable GPU with H.264 encode support (Intel, AMD) +# System packages: libva-dev, libgbm-dev + +name: VA-API H.264 Color Bars (MoQ Stream) +description: Continuously generates SMPTE color bars and streams via MoQ using VA-API H.264 HW encoder +mode: dynamic +client: + gateway_path: /moq/video + watch: + broadcast: output + audio: false + video: true + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + pixel_format: nv12 + draw_time: true + + vaapi_h264_encoder: + kind: video::vaapi::h264_encoder + params: + quality: 26 + framerate: 30 + needs: colorbars + + moq_peer: + kind: transport::moq::peer + params: + gateway_path: /moq/video + output_broadcast: output + allow_reconnect: true + video_codec: h264 + needs: + in: vaapi_h264_encoder diff --git a/samples/pipelines/oneshot/video_vaapi_h264_colorbars.yml b/samples/pipelines/oneshot/video_vaapi_h264_colorbars.yml new file mode 100644 index 00000000..ac6f513d --- /dev/null +++ b/samples/pipelines/oneshot/video_vaapi_h264_colorbars.yml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Demonstrates the VA-API H.264 HW encoder: +# Generates SMPTE color bars (NV12), encodes to H.264 via VA-API +# (GPU-accelerated), muxes into an MP4 container, and writes the result +# to HTTP output. +# +# Requires: skit built with --features vaapi +# VA-API capable GPU with H.264 encode support (Intel, AMD) +# System packages: libva-dev, libgbm-dev + +name: VA-API H.264 Encode (MP4 Oneshot) +description: Generates color bars, encodes to H.264 using VA-API HW encoder, and muxes into MP4 (30 seconds) +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 1280 + height: 720 + fps: 30 + frame_count: 900 # 30 seconds at 30fps + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + vaapi_h264_encoder: + kind: video::vaapi::h264_encoder + params: + quality: 26 + framerate: 30 + needs: colorbars + + mp4_muxer: + kind: containers::mp4::muxer + params: + mode: stream + video_width: 1280 + video_height: 720 + needs: vaapi_h264_encoder + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/mp4; codecs="avc1.4d0028"' + needs: mp4_muxer From c946f52fe941456e95239fb50c45e5c9f0a80a89 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 18:03:17 +0000 Subject: [PATCH 15/69] feat(nodes): add VA-API H.264 encoder and decoder nodes Add vaapi_h264 module with VaapiH264EncoderNode and VaapiH264DecoderNode using cros-codecs StatelessEncoder/StatelessDecoder for H.264 via VA-API. - Encoder: CQP rate control, Main profile, macroblock-aligned coding - Decoder: stateless H.264 decode with format-change handling - Reuses shared helpers from vaapi_av1 (GBM/NV12 I/O, device detection) - Registration: video::vaapi::h264_encoder, video::vaapi::h264_decoder - Sample pipelines: oneshot MP4 + dynamic MoQ for VA-API H.264 Supported on Intel (Sandy Bridge+), AMD, and NVIDIA (decode only). Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_h264.rs | 103 ++++++++------------------- 1 file changed, 28 insertions(+), 75 deletions(-) diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs index f4373538..b80fbdec 100644 --- a/crates/nodes/src/video/vaapi_h264.rs +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -117,10 +117,7 @@ pub struct VaapiH264DecoderConfig { impl Default for VaapiH264DecoderConfig { fn default() -> Self { - Self { - render_device: None, - hw_accel: HwAccelMode::Auto, - } + Self { render_device: None, hw_accel: HwAccelMode::Auto } } } @@ -268,7 +265,7 @@ fn vaapi_h264_decode_loop( Err(e) => { let _ = result_tx.blocking_send(Err(e)); return; - } + }, }; tracing::info!(device = %path, "VA-API H.264 decoder opened display"); @@ -282,7 +279,7 @@ fn vaapi_h264_decode_loop( let _ = result_tx.blocking_send(Err(format!("failed to create VA-API H.264 decoder: {e}"))); return; - } + }, }; // Stream resolution — updated on FormatChanged events. @@ -324,16 +321,15 @@ fn vaapi_h264_decode_loop( Ok(bytes_consumed) => { offset += bytes_consumed; made_progress = true; - } + }, Err(DecodeError::CheckEvents | DecodeError::NotEnoughOutputBuffers(_)) => { // Process pending events / drain ready frames, then retry. - } + }, Err(e) => { tracing::error!(error = %e, "VA-API H.264 decode error"); - let _ = - result_tx.blocking_send(Err(format!("VA-API H.264 decode error: {e}"))); + let _ = result_tx.blocking_send(Err(format!("VA-API H.264 decode error: {e}"))); break; - } + }, } // Process all pending events (format changes + ready frames). @@ -381,13 +377,7 @@ fn vaapi_h264_decode_loop( if let Err(e) = decoder.flush() { tracing::warn!(error = %e, "VA-API H.264 decoder flush failed"); } - drain_decoder_events( - &mut decoder, - result_tx, - None, - &mut coded_width, - &mut coded_height, - ); + drain_decoder_events(&mut decoder, result_tx, None, &mut coded_width, &mut coded_height); } /// Drain all pending events from the decoder. @@ -420,7 +410,7 @@ fn drain_decoder_events( "VA-API H.264 decoder stream format changed" ); } - } + }, DecoderEvent::FrameReady(handle) => { if let Err(e) = handle.sync() { tracing::error!(error = %e, "VA-API H.264 frame sync failed"); @@ -441,7 +431,7 @@ fn drain_decoder_events( Err(e) => { tracing::error!(error = %e, "failed to map decoded GBM frame"); continue; - } + }, }; read_nv12_from_mapping(mapping.as_ref(), frame_w, frame_h, &pitches) }; @@ -457,15 +447,15 @@ fn drain_decoder_events( if result_tx.blocking_send(Ok(frame)).is_err() { return (true, had_events); } - } + }, Err(e) => { tracing::error!( error = %e, "failed to construct VideoFrame from decoded data" ); - } + }, } - } + }, } } (false, had_events) @@ -635,10 +625,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { let coded_height = align_up_u32(height, H264_MB_SIZE); let cros_config = CrosH264EncoderConfig { - resolution: CrosResolution { - width: coded_width, - height: coded_height, - }, + resolution: CrosResolution { width: coded_width, height: coded_height }, profile: H264Profile::Main, level: H264Level::L4, pred_structure: PredictionStructure::LowDelay { limit: 1024 }, @@ -654,10 +641,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { display, cros_config, nv12_fourcc(), - CrosResolution { - width: coded_width, - height: coded_height, - }, + CrosResolution { width: coded_width, height: coded_height }, config.low_power, BlockingMode::Blocking, ) @@ -673,15 +657,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { "VA-API H.264 encoder created" ); - Ok(Self { - encoder, - gbm, - width, - height, - coded_width, - coded_height, - frame_count: 0, - }) + Ok(Self { encoder, gbm, width, height, coded_width, coded_height, frame_count: 0 }) } fn encode( @@ -690,25 +666,17 @@ impl StandardVideoEncoder for VaapiH264Encoder { metadata: Option, ) -> Result, String> { if frame.pixel_format == PixelFormat::Rgba8 { - return Err( - "VA-API H.264 encoder requires NV12 or I420 input; \ + return Err("VA-API H.264 encoder requires NV12 or I420 input; \ insert a video::pixel_convert node upstream" - .into(), - ); + .into()); } // Create a GBM frame and upload the raw video data. let mut gbm_frame = Arc::clone(&self.gbm) .new_frame( nv12_fourcc(), - CrosResolution { - width: self.width, - height: self.height, - }, - CrosResolution { - width: self.coded_width, - height: self.coded_height, - }, + CrosResolution { width: self.width, height: self.height }, + CrosResolution { width: self.coded_width, height: self.coded_height }, GbmUsage::Encode, ) .map_err(|e| format!("failed to allocate GBM frame for encoding: {e}"))?; @@ -732,16 +700,9 @@ impl StandardVideoEncoder for VaapiH264Encoder { let frame_layout = FrameLayout { format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR - size: CrosResolution { - width: self.coded_width, - height: self.coded_height, - }, + size: CrosResolution { width: self.coded_width, height: self.coded_height }, planes: vec![ - PlaneLayout { - buffer_index: 0, - offset: 0, - stride: y_stride, - }, + PlaneLayout { buffer_index: 0, offset: 0, stride: y_stride }, PlaneLayout { buffer_index: 0, offset: uv_offset, @@ -768,7 +729,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { data: Bytes::from(coded.bitstream), metadata: metadata.clone(), }); - } + }, Ok(None) => break, Err(e) => return Err(format!("VA-API H.264 encoder poll error: {e}")), } @@ -778,9 +739,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { } fn flush_encoder(&mut self) -> Result, String> { - self.encoder - .drain() - .map_err(|e| format!("VA-API H.264 encoder drain error: {e}"))?; + self.encoder.drain().map_err(|e| format!("VA-API H.264 encoder drain error: {e}"))?; let mut packets = Vec::new(); loop { @@ -788,7 +747,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { Ok(Some(coded)) => { packets .push(EncodedPacket { data: Bytes::from(coded.bitstream), metadata: None }); - } + }, Ok(None) => break, Err(e) => return Err(format!("VA-API H.264 encoder poll error: {e}")), } @@ -821,10 +780,7 @@ pub fn register_vaapi_h264_nodes(registry: &mut NodeRegistry) { }, serde_json::to_value(schema_for!(VaapiH264DecoderConfig)) .expect("VaapiH264DecoderConfig schema should serialize to JSON"), - StaticPins { - inputs: default_decoder.input_pins(), - outputs: default_decoder.output_pins(), - }, + StaticPins { inputs: default_decoder.input_pins(), outputs: default_decoder.output_pins() }, vec![ "video".to_string(), "codecs".to_string(), @@ -848,10 +804,7 @@ pub fn register_vaapi_h264_nodes(registry: &mut NodeRegistry) { }, serde_json::to_value(schema_for!(VaapiH264EncoderConfig)) .expect("VaapiH264EncoderConfig schema should serialize to JSON"), - StaticPins { - inputs: default_encoder.input_pins(), - outputs: default_encoder.output_pins(), - }, + StaticPins { inputs: default_encoder.input_pins(), outputs: default_encoder.output_pins() }, vec![ "video".to_string(), "codecs".to_string(), @@ -1054,7 +1007,7 @@ mod tests { assert_eq!(frame.height, 64); assert_eq!(frame.pixel_format, PixelFormat::Nv12); assert!(!frame.data().is_empty(), "Decoded frame should have data"); - } + }, _ => panic!("Expected Video packet from VA-API H.264 decoder"), } } From 611f493b370588988b22bce1ccbd38e36359b0cd Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 18:33:44 +0000 Subject: [PATCH 16/69] fix(nodes): auto-detect VA-API H.264 encoder entrypoint Modern Intel GPUs (Gen 9+ / Skylake onwards) only expose the low-power fixed-function encoder (VAEntrypointEncSliceLP), not the full encoder (VAEntrypointEncSlice). Query the driver for supported entrypoints and auto-select the correct one instead of hardcoding low_power=false. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_h264.rs | 42 +++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs index b80fbdec..921d93a9 100644 --- a/crates/nodes/src/video/vaapi_h264.rs +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -624,6 +624,46 @@ impl StandardVideoEncoder for VaapiH264Encoder { let coded_width = align_up_u32(width, H264_MB_SIZE); let coded_height = align_up_u32(height, H264_MB_SIZE); + // Auto-detect the correct entrypoint. Modern Intel GPUs (Gen 9+ / + // Skylake onwards) only expose the low-power fixed-function encoder + // (`VAEntrypointEncSliceLP`), while older hardware and some AMD + // drivers use `VAEntrypointEncSlice`. Query the driver and pick + // whichever is available, preferring the config value when set. + let low_power = { + use libva::VAEntrypoint::{VAEntrypointEncSlice, VAEntrypointEncSliceLP}; + use libva::VAProfile::VAProfileH264Main; + + let entrypoints = display + .query_config_entrypoints(VAProfileH264Main) + .map_err(|e| format!("failed to query H.264 entrypoints: {e}"))?; + + let has_lp = entrypoints.contains(&VAEntrypointEncSliceLP); + let has_full = entrypoints.contains(&VAEntrypointEncSlice); + + if !has_lp && !has_full { + return Err( + "VA-API driver does not support H.264 encoding (no EncSlice entrypoint)".into(), + ); + } + + // Prefer the user's explicit config; otherwise auto-detect. + if config.low_power { + if !has_lp { + return Err( + "low_power=true requested but VAEntrypointEncSliceLP is not supported" + .into(), + ); + } + true + } else if has_lp && !has_full { + // Driver only supports low-power (common on modern Intel). + tracing::info!("auto-selecting low-power H.264 encoder (VAEntrypointEncSliceLP)"); + true + } else { + false + } + }; + let cros_config = CrosH264EncoderConfig { resolution: CrosResolution { width: coded_width, height: coded_height }, profile: H264Profile::Main, @@ -642,7 +682,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { cros_config, nv12_fourcc(), CrosResolution { width: coded_width, height: coded_height }, - config.low_power, + low_power, BlockingMode::Blocking, ) .map_err(|e| format!("failed to create VA-API H.264 encoder: {e}"))?; From 23cf24ef04cabe251a45c704900475fc80463f96 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 18:54:55 +0000 Subject: [PATCH 17/69] fix(nodes): bypass GBM for VA-API encoders, use direct VA surfaces Replace GBM-backed frame allocation with direct VA surface creation and Image API uploads for both H.264 and AV1 VA-API encoders. The cros-codecs GBM allocator uses GBM_BO_USE_HW_VIDEO_ENCODER, a flag that Mesa's iris driver does not support for NV12 on some hardware (e.g. Intel Tiger Lake with Mesa 23.x), causing 'Error allocating contiguous buffer' failures. By using libva Surface<()> handles instead: - Surfaces are created via vaCreateSurfaces (no GBM needed) - NV12 data is uploaded via the VA Image API (vaCreateImage + vaPutImage) - The encoder's import_picture passthrough accepts Surface<()> directly - Pitches/offsets come from the VA driver's VAImage, not GBM This also adds two new shared helpers in vaapi_av1.rs: - open_va_display(): opens VA display without GBM device - write_nv12_to_va_surface(): uploads NV12/I420 frame data to a VA surface using the Image API, returning driver pitches/offsets Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 186 +++++++++++++++++++++------ crates/nodes/src/video/vaapi_h264.rs | 79 ++++++------ 2 files changed, 182 insertions(+), 83 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index f69af6e9..771d17cd 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -163,6 +163,118 @@ pub(super) fn open_va_and_gbm( Ok((display, gbm, path)) } +/// Open a VA display without a GBM device. +/// +/// Used by encoder paths that pass VA surfaces directly to the encoder, +/// bypassing GBM buffer allocation entirely. This avoids the +/// `GBM_BO_USE_HW_VIDEO_ENCODER` flag that Mesa's iris driver does not +/// support for NV12 on some hardware (e.g. Intel Tiger Lake). +pub(super) fn open_va_display( + render_device: Option<&String>, +) -> Result<(Rc, String), String> { + let path = resolve_render_device(render_device); + let display = libva::Display::open_drm_display(&path) + .map_err(|e| format!("failed to open VA display on {path}: {e}"))?; + Ok((display, path)) +} + +/// Write NV12 (or I420→NV12) data from a StreamKit [`VideoFrame`] into a VA +/// surface using the VA-API Image API. +/// +/// Uses `vaCreateImage` + `vaMapBuffer` to obtain a writable mapping, writes +/// NV12 data respecting the driver's internal pitches/offsets, then drops the +/// [`Image`] which flushes the data back via `vaPutImage`. +/// +/// Returns `(pitches, offsets)` — the per-plane stride and byte-offset arrays +/// from the `VAImage`, needed to build the [`FrameLayout`] for the encoder. +pub(super) fn write_nv12_to_va_surface( + display: &Rc, + surface: &libva::Surface<()>, + frame: &VideoFrame, +) -> Result<([usize; 2], [usize; 2]), String> { + let nv12_fourcc_val: u32 = nv12_fourcc().into(); + let image_fmts = display + .query_image_formats() + .map_err(|e| format!("failed to query VA image formats: {e}"))?; + let image_fmt = image_fmts + .into_iter() + .find(|f| f.fourcc == nv12_fourcc_val) + .ok_or("VA driver does not support NV12 image format")?; + + let mut image = libva::Image::create_from(surface, image_fmt, surface.size(), surface.size()) + .map_err(|e| format!("failed to create VA image for NV12 upload: {e}"))?; + + let va_image = *image.image(); + let y_pitch = va_image.pitches[0] as usize; + let uv_pitch = va_image.pitches[1] as usize; + let y_offset = va_image.offsets[0] as usize; + let uv_offset = va_image.offsets[1] as usize; + + let dest = image.as_mut(); + let src = frame.data.as_ref().as_ref(); + let w = frame.width as usize; + let h = frame.height as usize; + + match frame.pixel_format { + PixelFormat::Nv12 => { + // Y plane. + for row in 0..h { + let s = row * w; + let d = y_offset + row * y_pitch; + if s + w <= src.len() && d + w <= dest.len() { + dest[d..d + w].copy_from_slice(&src[s..s + w]); + } + } + // UV plane (already interleaved in NV12). + let uv_h = h / 2; + let src_uv = &src[w * h..]; + for row in 0..uv_h { + let s = row * w; + let d = uv_offset + row * uv_pitch; + if s + w <= src_uv.len() && d + w <= dest.len() { + dest[d..d + w].copy_from_slice(&src_uv[s..s + w]); + } + } + }, + PixelFormat::I420 => { + // Y plane — same as NV12. + for row in 0..h { + let s = row * w; + let d = y_offset + row * y_pitch; + if s + w <= src.len() && d + w <= dest.len() { + dest[d..d + w].copy_from_slice(&src[s..s + w]); + } + } + // I420 → NV12: interleave U and V into a single UV plane. + let uv_w = w / 2; + let uv_h = h / 2; + let u_start = w * h; + let v_start = u_start + uv_w * uv_h; + for row in 0..uv_h { + for col in 0..uv_w { + let u_idx = u_start + row * uv_w + col; + let v_idx = v_start + row * uv_w + col; + let d = uv_offset + row * uv_pitch + col * 2; + if u_idx < src.len() && v_idx < src.len() && d + 1 < dest.len() { + dest[d] = src[u_idx]; + dest[d + 1] = src[v_idx]; + } + } + } + }, + other => { + drop(image); + return Err(format!("write_nv12_to_va_surface: unsupported pixel format {other:?}")); + }, + } + + // Sync the surface before dropping the image (which calls vaPutImage). + surface.sync().map_err(|e| format!("VA surface sync failed: {e}"))?; + drop(image); + + Ok(([y_pitch, uv_pitch], [y_offset, uv_offset])) +} + /// Copy NV12 plane data from a GBM read-mapping into a flat `Vec` suitable /// for a packed StreamKit [`VideoFrame`]. /// @@ -846,14 +958,14 @@ impl EncoderNodeRunner for VaapiAv1EncoderNode { // Encoder — internal codec wrapper // --------------------------------------------------------------------------- -/// Type alias for the full VA-API AV1 encoder with GBM-backed frames. +/// Type alias for the VA-API AV1 encoder using direct VA surfaces. +/// +/// Bypasses GBM buffer allocation entirely — see the H.264 encoder type alias +/// in `vaapi_h264.rs` for the full rationale. type CrosVaapiAv1Encoder = StatelessEncoder< cros_codecs::encoder::av1::AV1, - GbmVideoFrame, - cros_codecs::backend::vaapi::encoder::VaapiBackend< - GbmExternalBufferDescriptor, - libva::Surface, - >, + libva::Surface<()>, + cros_codecs::backend::vaapi::encoder::VaapiBackend<(), libva::Surface<()>>, >; /// Internal encoder state wrapping the cros-codecs `StatelessEncoder`. @@ -862,7 +974,7 @@ type CrosVaapiAv1Encoder = StatelessEncoder< /// a `spawn_blocking` thread, matching the pattern in `av1.rs`. struct VaapiAv1Encoder { encoder: CrosVaapiAv1Encoder, - gbm: Arc, + display: Rc, width: u32, height: u32, coded_width: u32, @@ -875,7 +987,7 @@ impl StandardVideoEncoder for VaapiAv1Encoder { const CODEC_NAME: &'static str = "VA-API AV1"; fn new_encoder(width: u32, height: u32, config: &Self::Config) -> Result { - let (display, gbm, path) = open_va_and_gbm(config.render_device.as_ref())?; + let (display, path) = open_va_display(config.render_device.as_ref())?; tracing::info!(device = %path, width, height, "VA-API AV1 encoder opening"); let coded_width = align_up_u32(width, AV1_SB_SIZE); @@ -895,7 +1007,7 @@ impl StandardVideoEncoder for VaapiAv1Encoder { }; let encoder = CrosVaapiAv1Encoder::new_vaapi( - display, + Rc::clone(&display), cros_config, nv12_fourcc(), CrosResolution { width: coded_width, height: coded_height }, @@ -914,7 +1026,7 @@ impl StandardVideoEncoder for VaapiAv1Encoder { "VA-API AV1 encoder created" ); - Ok(Self { encoder, gbm, width, height, coded_width, coded_height, frame_count: 0 }) + Ok(Self { encoder, display, width, height, coded_width, coded_height, frame_count: 0 }) } fn encode( @@ -928,46 +1040,36 @@ impl StandardVideoEncoder for VaapiAv1Encoder { .into()); } - // Create a GBM frame and upload the raw video data. - let mut gbm_frame = Arc::clone(&self.gbm) - .new_frame( - nv12_fourcc(), - CrosResolution { width: self.width, height: self.height }, - CrosResolution { width: self.coded_width, height: self.coded_height }, - GbmUsage::Encode, + // Create a VA surface and upload NV12 data via the Image API. + // This bypasses GBM buffer allocation (GBM_BO_USE_HW_VIDEO_ENCODER), + // which Mesa's iris driver does not support for NV12 on all hardware. + let nv12_fourcc_val: u32 = nv12_fourcc().into(); + let mut surfaces = self + .display + .create_surfaces( + libva::VA_RT_FORMAT_YUV420, + Some(nv12_fourcc_val), + self.coded_width, + self.coded_height, + Some(libva::UsageHint::USAGE_HINT_ENCODER), + vec![()], ) - .map_err(|e| format!("failed to allocate GBM frame for encoding: {e}"))?; - - // Write frame data into the GBM buffer. - let pitches = gbm_frame.get_plane_pitch(); - { - let mapping = gbm_frame - .map_mut() - .map_err(|e| format!("failed to map GBM frame for writing: {e}"))?; - write_nv12_to_mapping(mapping.as_ref(), frame, &pitches)?; - } + .map_err(|e| format!("failed to create VA surface for encoding: {e}"))?; + let surface = + surfaces.pop().ok_or_else(|| "create_surfaces returned empty vec".to_string())?; + + // Write frame data into the VA surface. + let (pitches, offsets) = write_nv12_to_va_surface(&self.display, &surface, frame)?; let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); - // Ideally we'd use `gbm_frame.get_plane_offset()` to get the real UV - // plane offset from the GBM allocator, but that method is private in - // cros-codecs 0.0.6. Fall back to computing it from pitch × coded_height, - // which is correct for linear (non-tiled) NV12 allocations — the common - // case for VA-API encode surfaces. - let y_stride = pitches.first().copied().unwrap_or(self.coded_width as usize); - let uv_offset = y_stride * self.coded_height as usize; - let frame_layout = FrameLayout { format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR size: CrosResolution { width: self.coded_width, height: self.coded_height }, planes: vec![ - PlaneLayout { buffer_index: 0, offset: 0, stride: y_stride }, - PlaneLayout { - buffer_index: 0, - offset: uv_offset, - stride: pitches.get(1).copied().unwrap_or(self.coded_width as usize), - }, + PlaneLayout { buffer_index: 0, offset: offsets[0], stride: pitches[0] }, + PlaneLayout { buffer_index: 0, offset: offsets[1], stride: pitches[1] }, ], }; @@ -975,7 +1077,7 @@ impl StandardVideoEncoder for VaapiAv1Encoder { CrosFrameMetadata { timestamp, layout: frame_layout, force_keyframe: is_keyframe }; self.encoder - .encode(cros_meta, gbm_frame) + .encode(cros_meta, surface) .map_err(|e| format!("VA-API AV1 encode error: {e}"))?; self.frame_count += 1; diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs index 921d93a9..7285fd2c 100644 --- a/crates/nodes/src/video/vaapi_h264.rs +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -73,9 +73,10 @@ use super::HwAccelMode; use super::H264_CONTENT_TYPE; // Re-use helpers from the VA-API AV1 module — they are codec-agnostic NV12 -// I/O routines (GBM mapping, render-device detection, etc.). +// I/O routines (VA surface upload, GBM mapping, render-device detection, etc.). use super::vaapi_av1::{ - align_up_u32, nv12_fourcc, open_va_and_gbm, read_nv12_from_mapping, write_nv12_to_mapping, + align_up_u32, nv12_fourcc, open_va_and_gbm, open_va_display, read_nv12_from_mapping, + write_nv12_to_mapping, write_nv12_to_va_surface, }; // --------------------------------------------------------------------------- @@ -589,14 +590,17 @@ impl EncoderNodeRunner for VaapiH264EncoderNode { // Encoder — internal codec wrapper // --------------------------------------------------------------------------- -/// Type alias for the full VA-API H.264 encoder with GBM-backed frames. +/// Type alias for the VA-API H.264 encoder using direct VA surfaces. +/// +/// Bypasses GBM buffer allocation entirely — input frames are uploaded to +/// VA surfaces via the VA-API Image API and passed straight through to the +/// encoder backend. This avoids the `GBM_BO_USE_HW_VIDEO_ENCODER` flag +/// which Mesa's iris driver does not support for NV12 on some hardware +/// (e.g. Intel Tiger Lake with Mesa 23.x). type CrosVaapiH264Encoder = StatelessEncoder< cros_codecs::encoder::h264::H264, - GbmVideoFrame, - cros_codecs::backend::vaapi::encoder::VaapiBackend< - GbmExternalBufferDescriptor, - libva::Surface, - >, + libva::Surface<()>, + cros_codecs::backend::vaapi::encoder::VaapiBackend<(), libva::Surface<()>>, >; /// Internal encoder state wrapping the cros-codecs `StatelessEncoder`. @@ -605,7 +609,7 @@ type CrosVaapiH264Encoder = StatelessEncoder< /// a `spawn_blocking` thread. struct VaapiH264Encoder { encoder: CrosVaapiH264Encoder, - gbm: Arc, + display: Rc, width: u32, height: u32, coded_width: u32, @@ -618,7 +622,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { const CODEC_NAME: &'static str = "VA-API H.264"; fn new_encoder(width: u32, height: u32, config: &Self::Config) -> Result { - let (display, gbm, path) = open_va_and_gbm(config.render_device.as_ref())?; + let (display, path) = open_va_display(config.render_device.as_ref())?; tracing::info!(device = %path, width, height, "VA-API H.264 encoder opening"); let coded_width = align_up_u32(width, H264_MB_SIZE); @@ -678,7 +682,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { }; let encoder = CrosVaapiH264Encoder::new_vaapi( - display, + Rc::clone(&display), cros_config, nv12_fourcc(), CrosResolution { width: coded_width, height: coded_height }, @@ -697,7 +701,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { "VA-API H.264 encoder created" ); - Ok(Self { encoder, gbm, width, height, coded_width, coded_height, frame_count: 0 }) + Ok(Self { encoder, display, width, height, coded_width, coded_height, frame_count: 0 }) } fn encode( @@ -711,43 +715,36 @@ impl StandardVideoEncoder for VaapiH264Encoder { .into()); } - // Create a GBM frame and upload the raw video data. - let mut gbm_frame = Arc::clone(&self.gbm) - .new_frame( - nv12_fourcc(), - CrosResolution { width: self.width, height: self.height }, - CrosResolution { width: self.coded_width, height: self.coded_height }, - GbmUsage::Encode, + // Create a VA surface and upload NV12 data via the Image API. + // This bypasses GBM buffer allocation (GBM_BO_USE_HW_VIDEO_ENCODER), + // which Mesa's iris driver does not support for NV12 on all hardware. + let nv12_fourcc_val: u32 = nv12_fourcc().into(); + let mut surfaces = self + .display + .create_surfaces( + libva::VA_RT_FORMAT_YUV420, + Some(nv12_fourcc_val), + self.coded_width, + self.coded_height, + Some(libva::UsageHint::USAGE_HINT_ENCODER), + vec![()], ) - .map_err(|e| format!("failed to allocate GBM frame for encoding: {e}"))?; - - // Write frame data into the GBM buffer. - let pitches = gbm_frame.get_plane_pitch(); - { - let mapping = gbm_frame - .map_mut() - .map_err(|e| format!("failed to map GBM frame for writing: {e}"))?; - write_nv12_to_mapping(mapping.as_ref(), frame, &pitches)?; - } + .map_err(|e| format!("failed to create VA surface for encoding: {e}"))?; + let surface = + surfaces.pop().ok_or_else(|| "create_surfaces returned empty vec".to_string())?; + + // Write frame data into the VA surface. + let (pitches, offsets) = write_nv12_to_va_surface(&self.display, &surface, frame)?; let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); - // Compute UV plane offset from pitch × coded_height (same approach as - // the AV1 encoder — get_plane_offset() is private in cros-codecs 0.0.6). - let y_stride = pitches.first().copied().unwrap_or(self.coded_width as usize); - let uv_offset = y_stride * self.coded_height as usize; - let frame_layout = FrameLayout { format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR size: CrosResolution { width: self.coded_width, height: self.coded_height }, planes: vec![ - PlaneLayout { buffer_index: 0, offset: 0, stride: y_stride }, - PlaneLayout { - buffer_index: 0, - offset: uv_offset, - stride: pitches.get(1).copied().unwrap_or(self.coded_width as usize), - }, + PlaneLayout { buffer_index: 0, offset: offsets[0], stride: pitches[0] }, + PlaneLayout { buffer_index: 0, offset: offsets[1], stride: pitches[1] }, ], }; @@ -755,7 +752,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { CrosFrameMetadata { timestamp, layout: frame_layout, force_keyframe: is_keyframe }; self.encoder - .encode(cros_meta, gbm_frame) + .encode(cros_meta, surface) .map_err(|e| format!("VA-API H.264 encode error: {e}"))?; self.frame_count += 1; From bcfd007e02bd1c27e30f19bd582b2f87a24b8df5 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 19:03:50 +0000 Subject: [PATCH 18/69] fix(nodes): use ceiling division for chroma dimensions in VA surface upload write_nv12_to_va_surface used truncating integer division (w / 2, h / 2) for chroma plane dimensions, which would corrupt chroma data for frames with odd width or height. VideoLayout::packed uses (width + 1) / 2 for chroma dimensions, so the upload function must match. Changes: - NV12 path: use (h+1)/2 for uv_h, ((w+1)/2)*2 for chroma row bytes - I420 path: use (w+1)/2 for uv_w, (h+1)/2 for uv_h This matches the existing write_nv12_to_mapping (which uses div_ceil) and i420_to_nv12_buffer in nv_av1.rs. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 771d17cd..84725951 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -226,13 +226,17 @@ pub(super) fn write_nv12_to_va_surface( } } // UV plane (already interleaved in NV12). - let uv_h = h / 2; + // Use ceiling division to handle odd dimensions, matching + // VideoLayout::packed which uses `(width + 1) / 2`. + let uv_h = (h + 1) / 2; + let chroma_row_bytes = ((w + 1) / 2) * 2; let src_uv = &src[w * h..]; for row in 0..uv_h { - let s = row * w; + let s = row * chroma_row_bytes; + let copy_w = chroma_row_bytes.min(w); let d = uv_offset + row * uv_pitch; - if s + w <= src_uv.len() && d + w <= dest.len() { - dest[d..d + w].copy_from_slice(&src_uv[s..s + w]); + if s + copy_w <= src_uv.len() && d + copy_w <= dest.len() { + dest[d..d + copy_w].copy_from_slice(&src_uv[s..s + copy_w]); } } }, @@ -246,8 +250,10 @@ pub(super) fn write_nv12_to_va_surface( } } // I420 → NV12: interleave U and V into a single UV plane. - let uv_w = w / 2; - let uv_h = h / 2; + // Use ceiling division to handle odd dimensions correctly, + // matching VideoLayout::packed and i420_to_nv12_buffer. + let uv_w = (w + 1) / 2; + let uv_h = (h + 1) / 2; let u_start = w * h; let v_start = u_start + uv_w * uv_h; for row in 0..uv_h { From 6442c756c41504e5c8a5e238b95b9f3843334131 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 19:10:00 +0000 Subject: [PATCH 19/69] fix(nodes): remove incorrect .min(w) clamp on NV12 UV row copy For odd-width frames, chroma_row_bytes (e.g. 642 for w=641) is the correct number of bytes per UV row in VideoLayout::packed format. Clamping to .min(w) would drop the last V sample on every UV row. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 84725951..40c0e33c 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -233,10 +233,10 @@ pub(super) fn write_nv12_to_va_surface( let src_uv = &src[w * h..]; for row in 0..uv_h { let s = row * chroma_row_bytes; - let copy_w = chroma_row_bytes.min(w); let d = uv_offset + row * uv_pitch; - if s + copy_w <= src_uv.len() && d + copy_w <= dest.len() { - dest[d..d + copy_w].copy_from_slice(&src_uv[s..s + copy_w]); + if s + chroma_row_bytes <= src_uv.len() && d + chroma_row_bytes <= dest.len() { + dest[d..d + chroma_row_bytes] + .copy_from_slice(&src_uv[s..s + chroma_row_bytes]); } } }, From a6583e8f900b299a3ff3f6b1805bec59c513b434 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Thu, 9 Apr 2026 19:10:12 +0000 Subject: [PATCH 20/69] style(nodes): fix rustfmt for VA surface UV copy Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 40c0e33c..9eccd219 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -235,8 +235,7 @@ pub(super) fn write_nv12_to_va_surface( let s = row * chroma_row_bytes; let d = uv_offset + row * uv_pitch; if s + chroma_row_bytes <= src_uv.len() && d + chroma_row_bytes <= dest.len() { - dest[d..d + chroma_row_bytes] - .copy_from_slice(&src_uv[s..s + chroma_row_bytes]); + dest[d..d + chroma_row_bytes].copy_from_slice(&src_uv[s..s + chroma_row_bytes]); } } }, From 70347b9889a668a84101e28f953340ccdd22838d Mon Sep 17 00:00:00 2001 From: "staging-devin-ai-integration[bot]" <166158716+staging-devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 18:49:20 +0200 Subject: [PATCH 21/69] feat(e2e): add headless pipeline validation tests (#285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(registry): publish marketplace registry update (#283) * chore(registry): publish marketplace registry update * fix(marketplace): prevent plugin releases from becoming latest on GitHub Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --------- Signed-off-by: StreamKit Devin Co-authored-by: StreamKit Devin Co-authored-by: Claudio Costa * feat(e2e): add headless pipeline validation tests Add a Rust-based test framework for validating oneshot pipelines against a live skit server using ffprobe for output verification. No browser required. Architecture: - datatest-stable discovers .yml files in samples/pipelines/test/ - Each .yml has a companion .toml sidecar with expected output metadata - Tests POST the pipeline YAML to /api/v1/process, save the response, and validate codec, resolution, container format via ffprobe - HW codec tests (NVENC AV1, Vulkan Video H.264) are skipped gracefully when the required node kind is not registered on the server New files: - tests/pipeline-validation/ Standalone Rust test crate - samples/pipelines/test/*.yml 4 short test pipelines (30 frames) - samples/pipelines/test/*.toml Expected output metadata sidecars - justfile: test-pipelines recipe Usage: just test-pipelines http://localhost:4545 just test-pipelines http://localhost:4545 vp9 # filter by name Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * refactor(e2e): restructure test pipelines to one-dir-per-test layout Move from flat files to directory-based test layout: samples/pipelines/test//pipeline.yml samples/pipelines/test//expected.toml Each test is self-contained in its own directory, making it easier to add test-specific input media or extra config in the future. The datatest-stable harness now matches on 'pipeline.yml' recursively. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * feat(e2e): add SVT-AV1 test pipeline and CI integration - Add svt_av1_colorbars test pipeline (SW codec, requires svt_av1 feature) - Add pacer node to VP9 pipeline for consistency with other WebM pipelines - Add pipeline-validation job to e2e.yml CI workflow — runs SW codec tests (VP9, OpenH264, SVT-AV1) against a live skit server with ffprobe validation - GPU-specific tests (NVENC AV1, Vulkan Video H.264) are skipped in CI via the requires_node mechanism Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(ci): fail explicitly when skit server doesn't start Add HEALTHY flag to health check loop so the pipeline-validation CI job fails with a clear error instead of proceeding to run tests against a server that never became healthy. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * feat(e2e): add complete format coverage for pipeline validation tests Extend the pipeline validation framework to support audio-only tests and file upload pipelines, then add 8 new test cases covering all core codecs, muxers, and demuxers: Audio codec tests: - opus_roundtrip: Opus encode/decode via Ogg container - opus_mp4: Opus encode via MP4 container (file mode) - flac_decode: FLAC decoder (symphonia) → Opus/Ogg - mp3_decode: MP3 decoder (symphonia) → Opus/Ogg - wav_decode: WAV demuxer (symphonia) → Opus/Ogg Video codec/decoder tests: - rav1e_colorbars: rav1e AV1 encoder → WebM - vp9_roundtrip: VP9 encode → decode → re-encode roundtrip - dav1d_roundtrip: SVT-AV1 → dav1d decode → SVT-AV1 re-encode Framework changes: - Expected struct now supports audio-only tests (audio_codec, sample_rate, channels) and file uploads (input_file) - run_pipeline() accepts optional input file for multipart upload - validate_output() validates audio and/or video stream properties - Test audio fixtures (Ogg/Opus, FLAC, MP3, WAV) in fixtures/ Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * chore: add REUSE/SPDX license files for test audio fixtures Adds CC0-1.0 license companion files for the generated test tone audio fixtures (ogg, flac, mp3, wav) to satisfy the reuse-compliance check. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * feat(ci): add GPU pipeline validation job on self-hosted runner Adds a 'Pipeline Validation (GPU)' job to the E2E workflow that runs on the self-hosted GPU runner. This builds skit with gpu, svt_av1, and dav1d_static features, starts the server, and runs all pipeline validation tests. Currently the NVENC AV1 and Vulkan Video H.264 tests will skip gracefully since those features (nvcodec, vulkan_video) aren't on main yet. Once PR #279 merges, adding those features to the build command will enable full HW codec pipeline validation in CI. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(ci): use alternate port for GPU pipeline validation server The self-hosted GPU runner has a persistent skit instance on port 4545. Use port 4546 for the pipeline validation server to avoid 'Address already in use' errors. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(ci): add libssl-dev for GPU pipeline validation runner The pipeline-validation test crate depends on reqwest which pulls in openssl-sys. The self-hosted GPU runner needs libssl-dev installed. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(test): correct multipart field name and audio channel config - Use 'media' instead of 'file' for the multipart field name to match the server's http_input binding convention. - Set channels: 2 on all opus_encoder nodes since test fixtures are stereo, fixing 'Incompatible connection' errors on the GPU runner. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(test): use mono audio fixtures to match Opus encoder pin type The Opus encoder node's input pin is hardcoded to accept mono audio (channels: 1). Regenerate all test fixtures as mono sine waves and update pipeline configs and expected.toml files accordingly. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(ci): add cleanup step to kill skit on self-hosted runner Self-hosted runners persist between runs, so background processes can accumulate. Add an always-run cleanup step to kill the skit process after tests complete. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(test): remove incompatible audio decode pipelines and fix GPU cleanup Remove flac_decode, mp3_decode, and wav_decode test pipelines: the FLAC decoder, MP3 decoder, and WAV demuxer all declare channels: 2 in their static output pins, but the Opus encoder (the only audio encoder) only accepts channels: 1. This static type mismatch causes pipeline validation to reject the connection before any audio data flows. Audio codec coverage is retained via opus_roundtrip (Ogg) and opus_mp4 (MP4) tests which exercise the full Opus encode/decode path. Also fix GPU CI cleanup: use PID-based kill instead of pkill pattern matching (the port number is in an env var, not the command line). Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * feat(ci): enable nvcodec + vulkan_video in GPU pipeline validation Now that the branch is rebased on top of PR #279 (HW video codecs), enable the nvcodec and vulkan_video features in the GPU CI build so the nv_av1_colorbars and vulkan_video_h264_colorbars tests actually run on the self-hosted GPU runner. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa * fix(test): correct doc comment for multipart field name The doc comment said 'file' but the code uses 'media'. Signed-off-by: Devin AI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --------- Signed-off-by: StreamKit Devin Signed-off-by: Devin AI Co-authored-by: streamkit-bot Co-authored-by: StreamKit Devin Co-authored-by: Claudio Costa --- .github/workflows/e2e.yml | 172 ++ .github/workflows/marketplace-release.yml | 1 + dist/registry/index.json | 14 + .../plugins/parakeet/0.1.0/manifest.json | 57 + .../plugins/parakeet/0.1.0/manifest.minisig | 4 + docs/public/registry/index.json | 14 + .../plugins/parakeet/0.1.0/manifest.json | 57 + .../plugins/parakeet/0.1.0/manifest.minisig | 4 + justfile | 12 + .../test/dav1d_roundtrip/expected.toml | 11 + .../test/dav1d_roundtrip/pipeline.yml | 66 + samples/pipelines/test/fixtures/test_tone.ogg | Bin 0 -> 19874 bytes .../test/fixtures/test_tone.ogg.license | 3 + .../test/nv_av1_colorbars/expected.toml | 14 + .../test/nv_av1_colorbars/pipeline.yml | 55 + .../test/openh264_colorbars/expected.toml | 12 + .../test/openh264_colorbars/pipeline.yml | 48 + samples/pipelines/test/opus_mp4/expected.toml | 10 + samples/pipelines/test/opus_mp4/pipeline.yml | 47 + .../test/opus_roundtrip/expected.toml | 10 + .../test/opus_roundtrip/pipeline.yml | 44 + .../test/rav1e_colorbars/expected.toml | 11 + .../test/rav1e_colorbars/pipeline.yml | 53 + .../test/svt_av1_colorbars/expected.toml | 11 + .../test/svt_av1_colorbars/pipeline.yml | 54 + .../test/vp9_colorbars/expected.toml | 12 + .../pipelines/test/vp9_colorbars/pipeline.yml | 49 + .../test/vp9_roundtrip/expected.toml | 10 + .../pipelines/test/vp9_roundtrip/pipeline.yml | 58 + .../vulkan_video_h264_colorbars/expected.toml | 14 + .../vulkan_video_h264_colorbars/pipeline.yml | 51 + tests/pipeline-validation/Cargo.lock | 1978 +++++++++++++++++ tests/pipeline-validation/Cargo.toml | 30 + tests/pipeline-validation/src/lib.rs | 300 +++ tests/pipeline-validation/tests/validate.rs | 112 + 35 files changed, 3398 insertions(+) create mode 100644 dist/registry/plugins/parakeet/0.1.0/manifest.json create mode 100644 dist/registry/plugins/parakeet/0.1.0/manifest.minisig create mode 100644 docs/public/registry/plugins/parakeet/0.1.0/manifest.json create mode 100644 docs/public/registry/plugins/parakeet/0.1.0/manifest.minisig create mode 100644 samples/pipelines/test/dav1d_roundtrip/expected.toml create mode 100644 samples/pipelines/test/dav1d_roundtrip/pipeline.yml create mode 100644 samples/pipelines/test/fixtures/test_tone.ogg create mode 100644 samples/pipelines/test/fixtures/test_tone.ogg.license create mode 100644 samples/pipelines/test/nv_av1_colorbars/expected.toml create mode 100644 samples/pipelines/test/nv_av1_colorbars/pipeline.yml create mode 100644 samples/pipelines/test/openh264_colorbars/expected.toml create mode 100644 samples/pipelines/test/openh264_colorbars/pipeline.yml create mode 100644 samples/pipelines/test/opus_mp4/expected.toml create mode 100644 samples/pipelines/test/opus_mp4/pipeline.yml create mode 100644 samples/pipelines/test/opus_roundtrip/expected.toml create mode 100644 samples/pipelines/test/opus_roundtrip/pipeline.yml create mode 100644 samples/pipelines/test/rav1e_colorbars/expected.toml create mode 100644 samples/pipelines/test/rav1e_colorbars/pipeline.yml create mode 100644 samples/pipelines/test/svt_av1_colorbars/expected.toml create mode 100644 samples/pipelines/test/svt_av1_colorbars/pipeline.yml create mode 100644 samples/pipelines/test/vp9_colorbars/expected.toml create mode 100644 samples/pipelines/test/vp9_colorbars/pipeline.yml create mode 100644 samples/pipelines/test/vp9_roundtrip/expected.toml create mode 100644 samples/pipelines/test/vp9_roundtrip/pipeline.yml create mode 100644 samples/pipelines/test/vulkan_video_h264_colorbars/expected.toml create mode 100644 samples/pipelines/test/vulkan_video_h264_colorbars/pipeline.yml create mode 100644 tests/pipeline-validation/Cargo.lock create mode 100644 tests/pipeline-validation/Cargo.toml create mode 100644 tests/pipeline-validation/src/lib.rs create mode 100644 tests/pipeline-validation/tests/validate.rs diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 0f67e590..45d5f59a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -12,6 +12,178 @@ env: RUSTC_WRAPPER: sccache jobs: + pipeline-validation: + name: Pipeline Validation + runs-on: ubuntu-22.04 + steps: + - name: Free disk space + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: true + dotnet: true + haskell: true + large-packages: false + docker-images: true + swap-storage: false + + - uses: actions/checkout@v5 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.3.5" + + - name: Build UI + working-directory: ./ui + run: | + bun install --frozen-lockfile + bun run build + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: "1.92.0" + + - uses: mozilla-actions/sccache-action@v0.0.9 + + - uses: Swatinem/rust-cache@v2 + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libvpx-dev nasm cmake ninja-build yasm meson ffmpeg + + - name: Build and install SVT-AV1 from source (v4.1.0) + run: | + cd /tmp + git clone --depth 1 --branch v4.1.0 https://gitlab.com/AOMediaCodec/SVT-AV1.git + cd SVT-AV1 + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr/local + cmake --build build -j"$(nproc)" + sudo cmake --install build + sudo ldconfig + + - name: Build skit + run: cargo build -p streamkit-server --bin skit --features "svt_av1 dav1d_static" + + - name: Start skit server + run: | + SK_SERVER__ADDRESS=127.0.0.1:4545 ./target/debug/skit & + # Wait for the server to be healthy + HEALTHY=0 + for i in $(seq 1 30); do + if curl -sf http://127.0.0.1:4545/healthz > /dev/null 2>&1; then + echo "skit is healthy" + HEALTHY=1 + break + fi + sleep 1 + done + if [ "$HEALTHY" -ne 1 ]; then + echo "ERROR: skit did not become healthy within 30s" + exit 1 + fi + + - name: Run pipeline validation tests (SW codecs only) + run: | + cd tests/pipeline-validation + PIPELINE_TEST_URL=http://127.0.0.1:4545 cargo test --test validate + + pipeline-validation-gpu: + name: Pipeline Validation (GPU) + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: self-hosted + steps: + - uses: actions/checkout@v5 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.3.5" + + - name: Build UI + working-directory: ./ui + run: | + bun install --frozen-lockfile + bun run build + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: "1.92.0" + + - uses: mozilla-actions/sccache-action@v0.0.9 + + - uses: Swatinem/rust-cache@v2 + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libvpx-dev nasm cmake ninja-build yasm meson ffmpeg libopus-dev pkg-config libssl-dev + + - name: Build and install SVT-AV1 from source (v4.1.0) + run: | + cd /tmp + if pkg-config --atleast-version=2.0.0 SvtAv1Enc 2>/dev/null; then + echo "SVT-AV1 already installed, skipping build" + else + rm -rf SVT-AV1 + git clone --depth 1 --branch v4.1.0 https://gitlab.com/AOMediaCodec/SVT-AV1.git + cd SVT-AV1 + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr/local + cmake --build build -j"$(nproc)" + sudo cmake --install build + sudo ldconfig + fi + + - name: Build skit (with GPU + HW codecs + SVT-AV1 + dav1d) + env: + VPX_LIB_DIR: /usr/lib/x86_64-linux-gnu + VPX_INCLUDE_DIR: /usr/include + VPX_VERSION: "1.13.0" + CUDA_INCLUDE_PATH: /usr/include + # bindgen (used by shiguredo_nvcodec) needs the clang builtin include + # path so it can find stddef.h and other compiler-provided headers. + BINDGEN_EXTRA_CLANG_ARGS: "-I/usr/lib/llvm-18/lib/clang/18/include" + run: | + cargo build -p streamkit-server --bin skit --features "gpu nvcodec vulkan_video svt_av1 dav1d_static" + + - name: Start skit server + run: | + # Use a non-default port to avoid conflicts with any persistent skit + # instance that may be running on the self-hosted runner. + SKIT_PORT=4546 + SK_SERVER__ADDRESS=127.0.0.1:${SKIT_PORT} ./target/debug/skit & + echo $! > /tmp/skit-gpu.pid + # Wait for the server to be healthy + HEALTHY=0 + for i in $(seq 1 30); do + if curl -sf http://127.0.0.1:${SKIT_PORT}/healthz > /dev/null 2>&1; then + echo "skit is healthy on port ${SKIT_PORT}" + HEALTHY=1 + break + fi + sleep 1 + done + if [ "$HEALTHY" -ne 1 ]; then + echo "ERROR: skit did not become healthy within 30s" + exit 1 + fi + + - name: Run pipeline validation tests (all codecs including GPU) + run: | + cd tests/pipeline-validation + PIPELINE_TEST_URL=http://127.0.0.1:4546 cargo test --test validate + + - name: Stop skit server + if: always() + run: | + if [ -f /tmp/skit-gpu.pid ]; then + kill "$(cat /tmp/skit-gpu.pid)" 2>/dev/null || true + rm -f /tmp/skit-gpu.pid + fi + e2e: name: Playwright E2E runs-on: ubuntu-22.04 diff --git a/.github/workflows/marketplace-release.yml b/.github/workflows/marketplace-release.yml index ef6ff7d5..8bd11c55 100644 --- a/.github/workflows/marketplace-release.yml +++ b/.github/workflows/marketplace-release.yml @@ -97,6 +97,7 @@ jobs: '--target', os.environ.get('GITHUB_SHA', 'HEAD'), '--title', release_name, '--notes', f'Plugin bundle for {name} v{version}.', + '--latest=false', bundle, ] if is_prerelease: diff --git a/dist/registry/index.json b/dist/registry/index.json index eb3c6b41..0095f84e 100644 --- a/dist/registry/index.json +++ b/dist/registry/index.json @@ -95,6 +95,20 @@ } ] }, + { + "id": "parakeet", + "name": "Parakeet TDT", + "description": "Fast speech-to-text using NVIDIA Parakeet TDT via sherpa-onnx", + "latest": "0.1.0", + "versions": [ + { + "version": "0.1.0", + "manifest_url": "https://streamkit.dev/registry/plugins/parakeet/0.1.0/manifest.json", + "signature_url": "https://streamkit.dev/registry/plugins/parakeet/0.1.0/manifest.minisig", + "published_at": "2026-04-10" + } + ] + }, { "id": "piper", "name": "Piper", diff --git a/dist/registry/plugins/parakeet/0.1.0/manifest.json b/dist/registry/plugins/parakeet/0.1.0/manifest.json new file mode 100644 index 00000000..64111cfb --- /dev/null +++ b/dist/registry/plugins/parakeet/0.1.0/manifest.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "id": "parakeet", + "name": "Parakeet TDT", + "version": "0.1.0", + "node_kind": "parakeet", + "kind": "native", + "description": "Fast speech-to-text using NVIDIA Parakeet TDT via sherpa-onnx", + "license": "MPL-2.0", + "homepage": "https://huggingface.co/nvidia/parakeet-tdt-0.6b-v2", + "entrypoint": "libparakeet.so", + "bundle": { + "url": "https://github.com/streamer45/streamkit/releases/download/plugin-parakeet-v0.1.0/parakeet-0.1.0-bundle.tar.zst", + "sha256": "9ca3cd97ba3c665517dd1319f8492c15b6b97549231577b640bbbc0f71aa60c3", + "size_bytes": 15635462 + }, + "models": [ + { + "id": "parakeet-tdt-0.6b-v2-int8", + "name": "Parakeet TDT 0.6B v2 (English, INT8)", + "default": true, + "source": "huggingface", + "repo_id": "streamkit/parakeet-models", + "revision": "main", + "files": [ + "encoder.int8.onnx", + "decoder.int8.onnx", + "joiner.int8.onnx", + "tokens.txt" + ], + "expected_size_bytes": 661190513, + "license": "CC-BY-4.0", + "license_url": "https://huggingface.co/nvidia/parakeet-tdt-0.6b-v2", + "file_checksums": { + "encoder.int8.onnx": "a32b12d17bbbc309d0686fbbcc2987b5e9b8333a7da83fa6b089f0a2acd651ab", + "decoder.int8.onnx": "b6bb64963457237b900e496ee9994b59294526439fbcc1fecf705b31a15c6b4e", + "joiner.int8.onnx": "7946164367946e7f9f29a122407c3252b680dbae9a51343eb2488d057c3c43d2", + "tokens.txt": "ec182b70dd42113aff6c5372c75cac58c952443eb22322f57bbd7f53977d497d" + } + }, + { + "id": "silero-vad", + "name": "Silero VAD (v6.2)", + "default": true, + "source": "huggingface", + "repo_id": "streamkit/parakeet-models", + "revision": "main", + "files": [ + "silero_vad.onnx" + ], + "expected_size_bytes": 2327524, + "license": "MIT", + "license_url": "https://github.com/snakers4/silero-vad/blob/master/LICENSE", + "sha256": "1a153a22f4509e292a94e67d6f9b85e8deb25b4988682b7e174c65279d8788e3" + } + ] +} \ No newline at end of file diff --git a/dist/registry/plugins/parakeet/0.1.0/manifest.minisig b/dist/registry/plugins/parakeet/0.1.0/manifest.minisig new file mode 100644 index 00000000..17252b4c --- /dev/null +++ b/dist/registry/plugins/parakeet/0.1.0/manifest.minisig @@ -0,0 +1,4 @@ +untrusted comment: signature from minisign secret key +RUQ/85JEqYXEgb3NHkk8EDvKfvijFiOhUMrmR97Tqb5NEYf3kTZTK+U5IHo10l0lOfInZaLjscG7wQ6OqfgO6gaRAnqlUzMXmgo= +trusted comment: timestamp:1775850398 file:manifest.json hashed +BMn5YTOa/ekLGbyRUiXr5cnwpYr8NrU+bQrKORovVYFIUtYDDwUyzvELqGjs/tShSlT8Ws7UVtBDdSVdJlOxAg== diff --git a/docs/public/registry/index.json b/docs/public/registry/index.json index eb3c6b41..0095f84e 100644 --- a/docs/public/registry/index.json +++ b/docs/public/registry/index.json @@ -95,6 +95,20 @@ } ] }, + { + "id": "parakeet", + "name": "Parakeet TDT", + "description": "Fast speech-to-text using NVIDIA Parakeet TDT via sherpa-onnx", + "latest": "0.1.0", + "versions": [ + { + "version": "0.1.0", + "manifest_url": "https://streamkit.dev/registry/plugins/parakeet/0.1.0/manifest.json", + "signature_url": "https://streamkit.dev/registry/plugins/parakeet/0.1.0/manifest.minisig", + "published_at": "2026-04-10" + } + ] + }, { "id": "piper", "name": "Piper", diff --git a/docs/public/registry/plugins/parakeet/0.1.0/manifest.json b/docs/public/registry/plugins/parakeet/0.1.0/manifest.json new file mode 100644 index 00000000..64111cfb --- /dev/null +++ b/docs/public/registry/plugins/parakeet/0.1.0/manifest.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "id": "parakeet", + "name": "Parakeet TDT", + "version": "0.1.0", + "node_kind": "parakeet", + "kind": "native", + "description": "Fast speech-to-text using NVIDIA Parakeet TDT via sherpa-onnx", + "license": "MPL-2.0", + "homepage": "https://huggingface.co/nvidia/parakeet-tdt-0.6b-v2", + "entrypoint": "libparakeet.so", + "bundle": { + "url": "https://github.com/streamer45/streamkit/releases/download/plugin-parakeet-v0.1.0/parakeet-0.1.0-bundle.tar.zst", + "sha256": "9ca3cd97ba3c665517dd1319f8492c15b6b97549231577b640bbbc0f71aa60c3", + "size_bytes": 15635462 + }, + "models": [ + { + "id": "parakeet-tdt-0.6b-v2-int8", + "name": "Parakeet TDT 0.6B v2 (English, INT8)", + "default": true, + "source": "huggingface", + "repo_id": "streamkit/parakeet-models", + "revision": "main", + "files": [ + "encoder.int8.onnx", + "decoder.int8.onnx", + "joiner.int8.onnx", + "tokens.txt" + ], + "expected_size_bytes": 661190513, + "license": "CC-BY-4.0", + "license_url": "https://huggingface.co/nvidia/parakeet-tdt-0.6b-v2", + "file_checksums": { + "encoder.int8.onnx": "a32b12d17bbbc309d0686fbbcc2987b5e9b8333a7da83fa6b089f0a2acd651ab", + "decoder.int8.onnx": "b6bb64963457237b900e496ee9994b59294526439fbcc1fecf705b31a15c6b4e", + "joiner.int8.onnx": "7946164367946e7f9f29a122407c3252b680dbae9a51343eb2488d057c3c43d2", + "tokens.txt": "ec182b70dd42113aff6c5372c75cac58c952443eb22322f57bbd7f53977d497d" + } + }, + { + "id": "silero-vad", + "name": "Silero VAD (v6.2)", + "default": true, + "source": "huggingface", + "repo_id": "streamkit/parakeet-models", + "revision": "main", + "files": [ + "silero_vad.onnx" + ], + "expected_size_bytes": 2327524, + "license": "MIT", + "license_url": "https://github.com/snakers4/silero-vad/blob/master/LICENSE", + "sha256": "1a153a22f4509e292a94e67d6f9b85e8deb25b4988682b7e174c65279d8788e3" + } + ] +} \ No newline at end of file diff --git a/docs/public/registry/plugins/parakeet/0.1.0/manifest.minisig b/docs/public/registry/plugins/parakeet/0.1.0/manifest.minisig new file mode 100644 index 00000000..17252b4c --- /dev/null +++ b/docs/public/registry/plugins/parakeet/0.1.0/manifest.minisig @@ -0,0 +1,4 @@ +untrusted comment: signature from minisign secret key +RUQ/85JEqYXEgb3NHkk8EDvKfvijFiOhUMrmR97Tqb5NEYf3kTZTK+U5IHo10l0lOfInZaLjscG7wQ6OqfgO6gaRAnqlUzMXmgo= +trusted comment: timestamp:1775850398 file:manifest.json hashed +BMn5YTOa/ekLGbyRUiXr5cnwpYr8NrU+bQrKORovVYFIUtYDDwUyzvELqGjs/tShSlT8Ws7UVtBDdSVdJlOxAg== diff --git a/justfile b/justfile index 967f9ba6..380929bf 100644 --- a/justfile +++ b/justfile @@ -1389,6 +1389,18 @@ e2e-external url filter='': @echo "Running E2E tests against {{url}}..." @cd e2e && E2E_BASE_URL={{url}} bun run test:only {{ if filter != "" { "--grep '" + filter + "'" } else { "" } }} +# Run headless pipeline validation tests (no browser required). +# Requires a running skit server — pass its URL as an argument. +# Each .yml in samples/pipelines/test/ becomes a test case; a companion +# .toml sidecar declares the expected ffprobe output. +# +# Usage: +# just test-pipelines http://localhost:4545 +# just test-pipelines http://localhost:4545 vp9 # filter by name +test-pipelines url filter='': + @echo "Running headless pipeline validation tests against {{url}}..." + @cd tests/pipeline-validation && PIPELINE_TEST_URL={{url}} cargo test --test validate {{ if filter != "" { "-- " + filter } else { "" } }} + # Show E2E test report [working-directory: 'e2e'] e2e-report: diff --git a/samples/pipelines/test/dav1d_roundtrip/expected.toml b/samples/pipelines/test/dav1d_roundtrip/expected.toml new file mode 100644 index 00000000..dcb94a18 --- /dev/null +++ b/samples/pipelines/test/dav1d_roundtrip/expected.toml @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +requires_node = "video::dav1d::decoder" +output_extension = ".webm" +codec_name = "av1" +width = 320 +height = 240 +container_format = "matroska,webm" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/dav1d_roundtrip/pipeline.yml b/samples/pipelines/test/dav1d_roundtrip/pipeline.yml new file mode 100644 index 00000000..ae3db3e9 --- /dev/null +++ b/samples/pipelines/test/dav1d_roundtrip/pipeline.yml @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: SVT-AV1 encode → dav1d decode → SVT-AV1 re-encode roundtrip. +# Exercises: colorbars → svt_av1 encoder → dav1d decoder → svt_av1 re-encoder → webm muxer +# Used by headless pipeline validation tests. +# +# Requires: skit built with --features "svt_av1 dav1d" + +name: Test — dav1d AV1 Decode Roundtrip +description: AV1 roundtrip via SVT-AV1 encode → dav1d decode → SVT-AV1 re-encode +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + svt_av1_encoder: + kind: video::svt_av1::encoder + params: + preset: 12 + low_latency: true + needs: colorbars + + dav1d_decoder: + kind: video::dav1d::decoder + needs: svt_av1_encoder + + svt_av1_reencoder: + kind: video::svt_av1::encoder + params: + preset: 12 + low_latency: true + needs: dav1d_decoder + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 320 + video_height: 240 + streaming_mode: live + needs: svt_av1_reencoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="av1"' + needs: pacer diff --git a/samples/pipelines/test/fixtures/test_tone.ogg b/samples/pipelines/test/fixtures/test_tone.ogg new file mode 100644 index 0000000000000000000000000000000000000000..818f8521164bfc1d24952e371b3349addf8f5912 GIT binary patch literal 19874 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 K>kf}YC4T@&JuFrL literal 0 HcmV?d00001 diff --git a/samples/pipelines/test/fixtures/test_tone.ogg.license b/samples/pipelines/test/fixtures/test_tone.ogg.license new file mode 100644 index 00000000..45469f0a --- /dev/null +++ b/samples/pipelines/test/fixtures/test_tone.ogg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: © 2025 StreamKit Contributors + +SPDX-License-Identifier: CC0-1.0 diff --git a/samples/pipelines/test/nv_av1_colorbars/expected.toml b/samples/pipelines/test/nv_av1_colorbars/expected.toml new file mode 100644 index 00000000..287d611d --- /dev/null +++ b/samples/pipelines/test/nv_av1_colorbars/expected.toml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Expected output metadata for nv_av1_colorbars.yml +# Requires: skit built with --features nvcodec + NVIDIA GPU + +requires_node = "video::nv::av1_encoder" +output_extension = ".webm" +codec_name = "av1" +width = 320 +height = 240 +container_format = "matroska,webm" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/nv_av1_colorbars/pipeline.yml b/samples/pipelines/test/nv_av1_colorbars/pipeline.yml new file mode 100644 index 00000000..053654b8 --- /dev/null +++ b/samples/pipelines/test/nv_av1_colorbars/pipeline.yml @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: NVENC AV1/WebM colorbars (30 frames = 1 second at 30fps). +# Used by headless pipeline validation tests. +# +# Requires: skit built with --features nvcodec +# NVIDIA GPU with NVENC AV1 support (Ada Lovelace / RTX 40+) + +name: Test — NVENC AV1 Color Bars +description: Short NVENC AV1/WebM colorbars for automated testing +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + nv_av1_encoder: + kind: video::nv::av1_encoder + params: + bitrate: 1000000 + framerate: 30 + needs: colorbars + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 320 + video_height: 240 + streaming_mode: live + needs: nv_av1_encoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="av1"' + needs: pacer diff --git a/samples/pipelines/test/openh264_colorbars/expected.toml b/samples/pipelines/test/openh264_colorbars/expected.toml new file mode 100644 index 00000000..21388536 --- /dev/null +++ b/samples/pipelines/test/openh264_colorbars/expected.toml @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Expected output metadata for openh264_colorbars.yml + +output_extension = ".mp4" +codec_name = "h264" +width = 320 +height = 240 +container_format = "mov,mp4,m4a,3gp,3g2,mj2" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/openh264_colorbars/pipeline.yml b/samples/pipelines/test/openh264_colorbars/pipeline.yml new file mode 100644 index 00000000..e42618cc --- /dev/null +++ b/samples/pipelines/test/openh264_colorbars/pipeline.yml @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: OpenH264/MP4 colorbars (30 frames = 1 second at 30fps). +# Used by headless pipeline validation tests. + +name: Test — OpenH264 Color Bars +description: Short H.264/MP4 colorbars for automated testing +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + h264_encoder: + kind: video::openh264::encoder + params: + bitrate_kbps: 1000 + max_frame_rate: 30.0 + needs: colorbars + + mp4_muxer: + kind: containers::mp4::muxer + params: + mode: stream + video_width: 320 + video_height: 240 + needs: h264_encoder + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/mp4; codecs="avc1.42c01f"' + needs: mp4_muxer diff --git a/samples/pipelines/test/opus_mp4/expected.toml b/samples/pipelines/test/opus_mp4/expected.toml new file mode 100644 index 00000000..81badb0e --- /dev/null +++ b/samples/pipelines/test/opus_mp4/expected.toml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +output_extension = ".mp4" +container_format = "mov,mp4,m4a,3gp,3g2,mj2" +audio_codec = "opus" +sample_rate = 48000 +channels = 1 +input_file = "../fixtures/test_tone.ogg" diff --git a/samples/pipelines/test/opus_mp4/pipeline.yml b/samples/pipelines/test/opus_mp4/pipeline.yml new file mode 100644 index 00000000..0ce01cd3 --- /dev/null +++ b/samples/pipelines/test/opus_mp4/pipeline.yml @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: Opus in MP4 container. +# Exercises: ogg demuxer → opus decoder → opus encoder → mp4 muxer (file mode) +# Used by headless pipeline validation tests. + +name: Test — Opus in MP4 +description: Decodes uploaded Ogg/Opus, re-encodes to Opus, muxes into MP4 +mode: oneshot +client: + input: + type: file_upload + accept: "audio/opus" + output: + type: audio + +nodes: + http_input: + kind: streamkit::http_input + + ogg_demuxer: + kind: containers::ogg::demuxer + needs: http_input + + opus_decoder: + kind: audio::opus::decoder + needs: ogg_demuxer + + opus_encoder: + kind: audio::opus::encoder + needs: opus_decoder + + mp4_muxer: + kind: containers::mp4::muxer + params: + mode: file + sample_rate: 48000 + channels: 1 + needs: opus_encoder + + http_output: + kind: streamkit::http_output + params: + content_type: 'audio/mp4; codecs="opus"' + needs: mp4_muxer diff --git a/samples/pipelines/test/opus_roundtrip/expected.toml b/samples/pipelines/test/opus_roundtrip/expected.toml new file mode 100644 index 00000000..b4c40adc --- /dev/null +++ b/samples/pipelines/test/opus_roundtrip/expected.toml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +output_extension = ".ogg" +container_format = "ogg" +audio_codec = "opus" +sample_rate = 48000 +channels = 1 +input_file = "../fixtures/test_tone.ogg" diff --git a/samples/pipelines/test/opus_roundtrip/pipeline.yml b/samples/pipelines/test/opus_roundtrip/pipeline.yml new file mode 100644 index 00000000..0419fa3a --- /dev/null +++ b/samples/pipelines/test/opus_roundtrip/pipeline.yml @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: Opus encode/decode roundtrip via Ogg container. +# Exercises: ogg demuxer → opus decoder → opus encoder → ogg muxer +# Used by headless pipeline validation tests. + +name: Test — Opus Roundtrip (Ogg) +description: Decodes uploaded Ogg/Opus, re-encodes to Opus, muxes into Ogg +mode: oneshot +client: + input: + type: file_upload + accept: "audio/opus" + output: + type: audio + +nodes: + http_input: + kind: streamkit::http_input + + ogg_demuxer: + kind: containers::ogg::demuxer + needs: http_input + + opus_decoder: + kind: audio::opus::decoder + needs: ogg_demuxer + + opus_encoder: + kind: audio::opus::encoder + needs: opus_decoder + + ogg_muxer: + kind: containers::ogg::muxer + params: + channels: 1 + chunk_size: 65536 + needs: opus_encoder + + http_output: + kind: streamkit::http_output + needs: ogg_muxer diff --git a/samples/pipelines/test/rav1e_colorbars/expected.toml b/samples/pipelines/test/rav1e_colorbars/expected.toml new file mode 100644 index 00000000..04fc7cb1 --- /dev/null +++ b/samples/pipelines/test/rav1e_colorbars/expected.toml @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +requires_node = "video::av1::encoder" +output_extension = ".webm" +codec_name = "av1" +width = 320 +height = 240 +container_format = "matroska,webm" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/rav1e_colorbars/pipeline.yml b/samples/pipelines/test/rav1e_colorbars/pipeline.yml new file mode 100644 index 00000000..df1fa0da --- /dev/null +++ b/samples/pipelines/test/rav1e_colorbars/pipeline.yml @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: rav1e AV1 encoder → WebM (30 frames = 1 second at 30fps). +# Exercises: colorbars → video::av1::encoder (rav1e, pure-Rust) → webm muxer +# Used by headless pipeline validation tests. + +name: Test — rav1e AV1 Color Bars +description: Short rav1e AV1/WebM colorbars for automated testing +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + av1_encoder: + kind: video::av1::encoder + params: + speed: 10 + low_latency: true + needs: colorbars + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 320 + video_height: 240 + streaming_mode: live + needs: av1_encoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="av1"' + needs: pacer diff --git a/samples/pipelines/test/svt_av1_colorbars/expected.toml b/samples/pipelines/test/svt_av1_colorbars/expected.toml new file mode 100644 index 00000000..afaa3247 --- /dev/null +++ b/samples/pipelines/test/svt_av1_colorbars/expected.toml @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +requires_node = "video::svt_av1::encoder" +output_extension = ".webm" +codec_name = "av1" +width = 320 +height = 240 +container_format = "matroska,webm" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/svt_av1_colorbars/pipeline.yml b/samples/pipelines/test/svt_av1_colorbars/pipeline.yml new file mode 100644 index 00000000..0004467e --- /dev/null +++ b/samples/pipelines/test/svt_av1_colorbars/pipeline.yml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: SVT-AV1/WebM colorbars (30 frames = 1 second at 30fps). +# Used by headless pipeline validation tests. +# +# Requires: skit built with --features svt_av1 + +name: Test — SVT-AV1 Color Bars +description: Short SVT-AV1/WebM colorbars for automated testing +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + svt_av1_encoder: + kind: video::svt_av1::encoder + params: + preset: 12 + low_latency: true + needs: colorbars + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 320 + video_height: 240 + streaming_mode: live + needs: svt_av1_encoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="av1"' + needs: pacer diff --git a/samples/pipelines/test/vp9_colorbars/expected.toml b/samples/pipelines/test/vp9_colorbars/expected.toml new file mode 100644 index 00000000..2df03262 --- /dev/null +++ b/samples/pipelines/test/vp9_colorbars/expected.toml @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Expected output metadata for vp9_colorbars.yml + +output_extension = ".webm" +codec_name = "vp9" +width = 320 +height = 240 +container_format = "matroska,webm" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/vp9_colorbars/pipeline.yml b/samples/pipelines/test/vp9_colorbars/pipeline.yml new file mode 100644 index 00000000..582d8d94 --- /dev/null +++ b/samples/pipelines/test/vp9_colorbars/pipeline.yml @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: VP9/WebM colorbars (30 frames = 1 second at 30fps). +# Used by headless pipeline validation tests. + +name: Test — VP9 Color Bars +description: Short VP9/WebM colorbars for automated testing +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + vp9_encoder: + kind: video::vp9::encoder + needs: colorbars + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 320 + video_height: 240 + streaming_mode: live + needs: vp9_encoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="vp9"' + needs: pacer diff --git a/samples/pipelines/test/vp9_roundtrip/expected.toml b/samples/pipelines/test/vp9_roundtrip/expected.toml new file mode 100644 index 00000000..b8322c5a --- /dev/null +++ b/samples/pipelines/test/vp9_roundtrip/expected.toml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +output_extension = ".webm" +codec_name = "vp9" +width = 320 +height = 240 +container_format = "matroska,webm" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/vp9_roundtrip/pipeline.yml b/samples/pipelines/test/vp9_roundtrip/pipeline.yml new file mode 100644 index 00000000..062d1804 --- /dev/null +++ b/samples/pipelines/test/vp9_roundtrip/pipeline.yml @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: VP9 encode → decode → re-encode roundtrip (30 frames). +# Exercises: colorbars → vp9 encoder → vp9 decoder → vp9 re-encoder → webm muxer +# Used by headless pipeline validation tests. + +name: Test — VP9 Encode/Decode Roundtrip +description: VP9 roundtrip via encode → decode → re-encode +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + vp9_encoder: + kind: video::vp9::encoder + needs: colorbars + + vp9_decoder: + kind: video::vp9::decoder + needs: vp9_encoder + + vp9_reencoder: + kind: video::vp9::encoder + needs: vp9_decoder + + webm_muxer: + kind: containers::webm::muxer + params: + video_width: 320 + video_height: 240 + streaming_mode: live + needs: vp9_reencoder + + pacer: + kind: core::pacer + needs: webm_muxer + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/webm; codecs="vp9"' + needs: pacer diff --git a/samples/pipelines/test/vulkan_video_h264_colorbars/expected.toml b/samples/pipelines/test/vulkan_video_h264_colorbars/expected.toml new file mode 100644 index 00000000..21b048d1 --- /dev/null +++ b/samples/pipelines/test/vulkan_video_h264_colorbars/expected.toml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Expected output metadata for vulkan_video_h264_colorbars.yml +# Requires: skit built with --features vulkan_video + Vulkan-capable GPU + +requires_node = "video::vulkan_video::h264_encoder" +output_extension = ".mp4" +codec_name = "h264" +width = 320 +height = 240 +container_format = "mov,mp4,m4a,3gp,3g2,mj2" +pix_fmt = "yuv420p" diff --git a/samples/pipelines/test/vulkan_video_h264_colorbars/pipeline.yml b/samples/pipelines/test/vulkan_video_h264_colorbars/pipeline.yml new file mode 100644 index 00000000..46491815 --- /dev/null +++ b/samples/pipelines/test/vulkan_video_h264_colorbars/pipeline.yml @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +# Test pipeline: Vulkan Video H.264/MP4 colorbars (30 frames = 1 second at 30fps). +# Used by headless pipeline validation tests. +# +# Requires: skit built with --features vulkan_video +# Vulkan-capable GPU with H.264 encode support + +name: Test — Vulkan Video H.264 Color Bars +description: Short Vulkan Video H.264/MP4 colorbars for automated testing +mode: oneshot +client: + input: + type: none + output: + type: video + +nodes: + colorbars: + kind: video::colorbars + params: + width: 320 + height: 240 + fps: 30 + frame_count: 30 + pixel_format: nv12 + draw_time: true + draw_time_use_pts: true + + vk_h264_encoder: + kind: video::vulkan_video::h264_encoder + params: + bitrate: 1000000 + framerate: 30 + needs: colorbars + + mp4_muxer: + kind: containers::mp4::muxer + params: + mode: stream + video_width: 320 + video_height: 240 + needs: vk_h264_encoder + + http_output: + kind: streamkit::http_output + params: + content_type: 'video/mp4; codecs="avc1.42c01f"' + needs: mp4_muxer diff --git a/tests/pipeline-validation/Cargo.lock b/tests/pipeline-validation/Cargo.lock new file mode 100644 index 00000000..d9d09f1a --- /dev/null +++ b/tests/pipeline-validation/Cargo.lock @@ -0,0 +1,1978 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "base64" +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", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" + +[[package]] +name = "cc" +version = "1.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "datatest-stable" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a867d7322eb69cf3a68a5426387a25b45cb3b9c5ee41023ee6cea92e2afadd82" +dependencies = [ + "camino", + "fancy-regex", + "libtest-mimic", + "walkdir", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "ffprobe" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ffef835e1f9ac151db5bb2adbb95c9dfe1f315f987f011dd89cd655b4e9a52c" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "libtest-mimic" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6ba06f0ade6e504aff834d7c34298e5155c6baca353cc6a4aaff2f9fd7f33" +dependencies = [ + "anstream", + "anstyle", + "clap", + "escape8259", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl" +version = "0.10.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pipeline-validation" +version = "0.1.0" +dependencies = [ + "datatest-stable", + "ffprobe", + "reqwest", + "serde", + "tempfile", + "toml", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/tests/pipeline-validation/Cargo.toml b/tests/pipeline-validation/Cargo.toml new file mode 100644 index 00000000..26f57935 --- /dev/null +++ b/tests/pipeline-validation/Cargo.toml @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: © 2025 StreamKit Contributors +# +# SPDX-License-Identifier: MPL-2.0 + +[package] +name = "pipeline-validation" +version = "0.1.0" +edition = "2021" +publish = false +description = "Headless pipeline validation tests for StreamKit" + +# No library crate — this package only contains integration tests. +[lib] +name = "pipeline_validation" +path = "src/lib.rs" + +[[test]] +name = "validate" +path = "tests/validate.rs" +harness = false + +[dependencies] +ffprobe = "0.4" +reqwest = { version = "0.12", features = ["blocking", "multipart", "json"] } +serde = { version = "1", features = ["derive"] } +toml = "0.8" +tempfile = "3" + +[dev-dependencies] +datatest-stable = "0.3.3" diff --git a/tests/pipeline-validation/src/lib.rs b/tests/pipeline-validation/src/lib.rs new file mode 100644 index 00000000..2d307789 --- /dev/null +++ b/tests/pipeline-validation/src/lib.rs @@ -0,0 +1,300 @@ +// SPDX-FileCopyrightText: © 2025 StreamKit Contributors +// +// SPDX-License-Identifier: MPL-2.0 + +//! Headless pipeline validation helpers for StreamKit. +//! +//! This crate provides utilities for running oneshot pipelines against a live +//! `skit` server, capturing the output, and validating it with `ffprobe`. +//! No browser required. +//! +//! # Architecture +//! +//! Each test case is defined by a pair of files in `samples/pipelines/test/`: +//! +//! - `.yml` — the pipeline YAML to POST to `/api/v1/process` +//! - `.toml` — expected output metadata (codec, resolution, container) +//! +//! The test harness discovers all `.yml` files, loads the companion `.toml`, +//! runs the pipeline, and validates the output with `ffprobe`. + +use std::collections::HashSet; +use std::io::Write; +use std::path::Path; + +use serde::Deserialize; + +/// Expected output metadata for a pipeline test case. +/// +/// Lives in a `.toml` sidecar file alongside each test pipeline YAML. +#[derive(Debug, Deserialize)] +pub struct Expected { + /// File extension for the output (e.g. ".webm", ".mp4", ".ogg"). + pub output_extension: String, + + /// Expected container format name from ffprobe (e.g. "matroska,webm", "mov,mp4,m4a,3gp,3g2,mj2"). + pub container_format: String, + + /// Optional: node kind that must be registered in the server for this test + /// to run. If the node is missing, the test is skipped (returns Ok). + pub requires_node: Option, + + // --- Video expectations (optional — omit for audio-only tests) --- + + /// Expected video codec name as reported by ffprobe (e.g. "vp9", "h264", "av1"). + pub codec_name: Option, + + /// Expected video width in pixels. + pub width: Option, + + /// Expected video height in pixels. + pub height: Option, + + /// Optional: expected pixel format (e.g. "yuv420p"). + pub pix_fmt: Option, + + // --- Audio expectations (optional — omit for video-only tests) --- + + /// Expected audio codec name as reported by ffprobe (e.g. "opus", "mp3", "flac"). + pub audio_codec: Option, + + /// Expected audio sample rate in Hz (e.g. 48000). + pub sample_rate: Option, + + /// Expected number of audio channels (e.g. 2). + pub channels: Option, + + // --- Input file (optional — for pipelines that accept file uploads) --- + + /// Relative path (from the test directory) to an input file to upload. + /// If set, the pipeline will be run with a file upload instead of no input. + pub input_file: Option, +} + +/// Get the base URL for the skit server from environment. +/// +/// Checks `PIPELINE_TEST_URL` first, then `E2E_BASE_URL`, and defaults to +/// `http://127.0.0.1:4545`. +pub fn get_base_url() -> String { + std::env::var("PIPELINE_TEST_URL") + .or_else(|_| std::env::var("E2E_BASE_URL")) + .unwrap_or_else(|_| "http://127.0.0.1:4545".to_string()) +} + +/// Query the server's node schema endpoint to discover which node kinds are +/// registered. Returns a set of node kind strings. +/// +/// This is used to skip tests for HW codecs that aren't compiled into the +/// running server binary (e.g. `video::nv::av1_encoder`). +pub fn get_available_nodes(base_url: &str) -> Result, String> { + let url = format!("{base_url}/api/v1/schema/nodes"); + let response = reqwest::blocking::get(&url) + .map_err(|e| format!("Failed to query node schema at {url}: {e}"))?; + + if !response.status().is_success() { + return Err(format!( + "Node schema request failed with status {}", + response.status() + )); + } + + #[derive(Deserialize)] + struct NodeInfo { + kind: String, + } + + let nodes: Vec = response + .json() + .map_err(|e| format!("Failed to parse node schema JSON: {e}"))?; + + Ok(nodes.into_iter().map(|n| n.kind).collect()) +} + +/// Run a oneshot pipeline against the skit server. +/// +/// Posts the pipeline YAML as a multipart form to `/api/v1/process` and +/// saves the streamed response body to a temporary file. +/// +/// If `input_file` is `Some`, the file is attached as the `media` part of the +/// multipart form (for pipelines that expect `client.input.type: file_upload`). +/// +/// Returns the path to the output file on success. +pub fn run_pipeline( + base_url: &str, + yaml_contents: &str, + output_extension: &str, + input_file: Option<&Path>, +) -> Result { + let url = format!("{base_url}/api/v1/process"); + + let mut form = reqwest::blocking::multipart::Form::new() + .text("config", yaml_contents.to_string()); + + // Attach the input file if provided. + if let Some(path) = input_file { + let file_bytes = std::fs::read(path) + .map_err(|e| format!("Failed to read input file {}: {e}", path.display()))?; + let file_name = path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("input") + .to_string(); + let part = reqwest::blocking::multipart::Part::bytes(file_bytes) + .file_name(file_name); + form = form.part("media", part); + } + + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(120)) + .build() + .map_err(|e| format!("Failed to create HTTP client: {e}"))?; + + let response = client + .post(&url) + .multipart(form) + .send() + .map_err(|e| format!("Pipeline request to {url} failed: {e}"))?; + + if !response.status().is_success() { + let status = response.status(); + let body = response + .text() + .unwrap_or_else(|_| "".to_string()); + return Err(format!("Pipeline returned {status}: {body}")); + } + + // Stream response to a temp file. + let mut tmp = tempfile::Builder::new() + .suffix(output_extension) + .tempfile() + .map_err(|e| format!("Failed to create temp file: {e}"))?; + + let bytes = response + .bytes() + .map_err(|e| format!("Failed to read response body: {e}"))?; + + tmp.write_all(&bytes) + .map_err(|e| format!("Failed to write output to temp file: {e}"))?; + + tmp.flush() + .map_err(|e| format!("Failed to flush temp file: {e}"))?; + + Ok(tmp) +} + +/// Validate a pipeline's output file against the expected metadata. +/// +/// Runs `ffprobe` against the output file and checks codec, resolution, +/// container format, and audio properties against the [`Expected`] values. +pub fn validate_output(output_path: &Path, expected: &Expected) -> Result<(), String> { + let probe = ffprobe::ffprobe(output_path) + .map_err(|e| format!("ffprobe failed on {}: {e}", output_path.display()))?; + + // Check container format. + let format_name = &probe.format.format_name; + if !format_name.contains(&expected.container_format) + && !expected.container_format.contains(format_name.as_str()) + { + return Err(format!( + "Container mismatch: expected format containing '{}', got '{}'", + expected.container_format, format_name + )); + } + + // --- Video validation (if video expectations are set) --- + if let Some(ref expected_codec) = expected.codec_name { + let video = probe + .streams + .iter() + .find(|s| s.codec_type.as_deref() == Some("video")) + .ok_or("No video stream found in output (expected video codec)")?; + + let codec_name = video.codec_name.as_deref().unwrap_or(""); + if codec_name != expected_codec.as_str() { + return Err(format!( + "Video codec mismatch: expected '{}', got '{}'", + expected_codec, codec_name + )); + } + + if let Some(expected_w) = expected.width { + let width = video.width.unwrap_or(0) as u32; + if width != expected_w { + return Err(format!( + "Video width mismatch: expected {expected_w}, got {width}" + )); + } + } + + if let Some(expected_h) = expected.height { + let height = video.height.unwrap_or(0) as u32; + if height != expected_h { + return Err(format!( + "Video height mismatch: expected {expected_h}, got {height}" + )); + } + } + + if let Some(ref expected_pix_fmt) = expected.pix_fmt { + let pix_fmt = video.pix_fmt.as_deref().unwrap_or(""); + if pix_fmt != expected_pix_fmt.as_str() { + return Err(format!( + "Pixel format mismatch: expected '{}', got '{}'", + expected_pix_fmt, pix_fmt + )); + } + } + } + + // --- Audio validation (if audio expectations are set) --- + if let Some(ref expected_audio_codec) = expected.audio_codec { + let audio = probe + .streams + .iter() + .find(|s| s.codec_type.as_deref() == Some("audio")) + .ok_or("No audio stream found in output (expected audio codec)")?; + + let codec_name = audio.codec_name.as_deref().unwrap_or(""); + if codec_name != expected_audio_codec.as_str() { + return Err(format!( + "Audio codec mismatch: expected '{}', got '{}'", + expected_audio_codec, codec_name + )); + } + + if let Some(expected_sr) = expected.sample_rate { + // ffprobe reports sample_rate as a string in the stream. + let actual_sr = audio + .sample_rate + .as_deref() + .and_then(|s| s.parse::().ok()) + .unwrap_or(0); + if actual_sr != expected_sr { + return Err(format!( + "Sample rate mismatch: expected {expected_sr}, got {actual_sr}" + )); + } + } + + if let Some(expected_ch) = expected.channels { + let actual_ch = audio.channels.unwrap_or(0) as u32; + if actual_ch != expected_ch { + return Err(format!( + "Channel count mismatch: expected {expected_ch}, got {actual_ch}" + )); + } + } + } + + // Ensure at least one stream type was validated. + if expected.codec_name.is_none() && expected.audio_codec.is_none() { + return Err( + "Expected TOML must specify at least one of 'codec_name' (video) or 'audio_codec' (audio)" + .to_string(), + ); + } + + Ok(()) +} + + diff --git a/tests/pipeline-validation/tests/validate.rs b/tests/pipeline-validation/tests/validate.rs new file mode 100644 index 00000000..82f1f12d --- /dev/null +++ b/tests/pipeline-validation/tests/validate.rs @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: © 2025 StreamKit Contributors +// +// SPDX-License-Identifier: MPL-2.0 + +//! Headless pipeline validation test harness. +//! +//! Discovers all `pipeline.yml` files under `samples/pipelines/test//`, +//! loads the sibling `expected.toml`, runs the pipeline against a live `skit` +//! server, and validates the output with `ffprobe`. +//! +//! # Usage +//! +//! ```bash +//! # Start skit, then run tests: +//! PIPELINE_TEST_URL=http://127.0.0.1:4545 \ +//! cargo test --manifest-path tests/pipeline-validation/Cargo.toml +//! +//! # Or via justfile: +//! just test-pipelines http://127.0.0.1:4545 +//! ``` + +#![allow(clippy::disallowed_macros)] // Test binary — no logging crate available. + +use std::collections::HashSet; +use std::path::Path; +use std::sync::OnceLock; + +use pipeline_validation::{ + get_available_nodes, get_base_url, run_pipeline, validate_output, Expected, +}; + +/// Lazily resolved base URL for the skit server. +fn base_url() -> &'static str { + static URL: OnceLock = OnceLock::new(); + URL.get_or_init(get_base_url) +} + +/// Lazily resolved set of available node kinds on the server. +fn available_nodes() -> &'static HashSet { + static NODES: OnceLock> = OnceLock::new(); + NODES.get_or_init(|| { + get_available_nodes(base_url()).unwrap_or_else(|err| { + eprintln!("WARNING: Could not query available nodes: {err}"); + eprintln!(" HW codec tests will be skipped."); + eprintln!(" Is skit running at {}?", base_url()); + HashSet::new() + }) + }) +} + +/// The main test function called by `datatest-stable` for each `pipeline.yml`. +/// +/// For each test directory it: +/// 1. Loads the sibling `expected.toml` +/// 2. Checks if the required node kind is available (skips if not) +/// 3. POSTs the pipeline to the server +/// 4. Saves the response to a temp file +/// 5. Validates the output with `ffprobe` +fn validate_pipeline(path: &Path, yaml: String) -> datatest_stable::Result<()> { + // Load sibling expectations file from the same directory. + let test_dir = path.parent().expect("pipeline.yml must be inside a test directory"); + let expected_path = test_dir.join("expected.toml"); + let expected_toml = std::fs::read_to_string(&expected_path).map_err(|e| { + format!( + "Missing expectations file '{}': {e}\n\ + Each test pipeline YAML needs a companion .toml with expected output metadata.", + expected_path.display() + ) + })?; + + let expected: Expected = toml::from_str(&expected_toml).map_err(|e| { + format!( + "Invalid expectations file '{}': {e}", + expected_path.display() + ) + })?; + + // Check if the required node is available (skip gracefully if not). + if let Some(ref required) = expected.requires_node { + if !available_nodes().contains(required.as_str()) { + eprintln!( + "SKIP: '{}' requires node '{}' which is not available on this server", + path.display(), + required + ); + return Ok(()); + } + } + + let test_name = test_dir + .file_name() + .map(|n| n.to_string_lossy().to_string()) + .unwrap_or_default(); + + // Resolve the input file path (if any) relative to the test directory. + let input_file = expected.input_file.as_ref().map(|rel| test_dir.join(rel)); + let input_ref = input_file.as_deref(); + + // Run the pipeline. + let output = run_pipeline(base_url(), &yaml, &expected.output_extension, input_ref) + .map_err(|e| format!("Pipeline '{test_name}' failed: {e}"))?; + + // Validate with ffprobe. + validate_output(output.path(), &expected) + .map_err(|e| format!("Validation failed for '{test_name}': {e}"))?; + + Ok(()) +} + +datatest_stable::harness! { + { test = validate_pipeline, root = "../../samples/pipelines/test", pattern = r"pipeline\.yml$" }, +} From 5bb1936a2efadf3e8e55097f57760f27538863d3 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 16:56:57 +0000 Subject: [PATCH 22/69] fix(e2e): improve pipeline test diagnostics and GPU CI reliability - Add file size to ffprobe error messages for easier debugging - Detect empty response bodies (encoder failed to produce output) - Capture skit server logs in GPU CI job for post-mortem analysis - Use --test-threads=1 for GPU tests to avoid NVENC session contention Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- .github/workflows/e2e.yml | 13 +++++++++++-- tests/pipeline-validation/src/lib.rs | 21 +++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 45d5f59a..ca4e4b8c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -154,7 +154,7 @@ jobs: # Use a non-default port to avoid conflicts with any persistent skit # instance that may be running on the self-hosted runner. SKIT_PORT=4546 - SK_SERVER__ADDRESS=127.0.0.1:${SKIT_PORT} ./target/debug/skit & + SK_SERVER__ADDRESS=127.0.0.1:${SKIT_PORT} ./target/debug/skit > /tmp/skit-gpu.log 2>&1 & echo $! > /tmp/skit-gpu.pid # Wait for the server to be healthy HEALTHY=0 @@ -174,7 +174,15 @@ jobs: - name: Run pipeline validation tests (all codecs including GPU) run: | cd tests/pipeline-validation - PIPELINE_TEST_URL=http://127.0.0.1:4546 cargo test --test validate + PIPELINE_TEST_URL=http://127.0.0.1:4546 cargo test --test validate -- --test-threads=1 + + - name: Show skit server logs + if: always() + run: | + if [ -f /tmp/skit-gpu.log ]; then + echo "=== skit server logs (last 100 lines) ===" + tail -100 /tmp/skit-gpu.log + fi - name: Stop skit server if: always() @@ -183,6 +191,7 @@ jobs: kill "$(cat /tmp/skit-gpu.pid)" 2>/dev/null || true rm -f /tmp/skit-gpu.pid fi + rm -f /tmp/skit-gpu.log e2e: name: Playwright E2E diff --git a/tests/pipeline-validation/src/lib.rs b/tests/pipeline-validation/src/lib.rs index 2d307789..4daedea6 100644 --- a/tests/pipeline-validation/src/lib.rs +++ b/tests/pipeline-validation/src/lib.rs @@ -173,6 +173,15 @@ pub fn run_pipeline( .bytes() .map_err(|e| format!("Failed to read response body: {e}"))?; + if bytes.is_empty() { + return Err( + "Pipeline returned HTTP 200 but the response body is empty. \ + This usually means the encoder failed to produce output \ + (e.g. the GPU does not support the required codec via this API)." + .to_string(), + ); + } + tmp.write_all(&bytes) .map_err(|e| format!("Failed to write output to temp file: {e}"))?; @@ -187,8 +196,16 @@ pub fn run_pipeline( /// Runs `ffprobe` against the output file and checks codec, resolution, /// container format, and audio properties against the [`Expected`] values. pub fn validate_output(output_path: &Path, expected: &Expected) -> Result<(), String> { - let probe = ffprobe::ffprobe(output_path) - .map_err(|e| format!("ffprobe failed on {}: {e}", output_path.display()))?; + let file_size = std::fs::metadata(output_path) + .map(|m| m.len()) + .unwrap_or(0); + + let probe = ffprobe::ffprobe(output_path).map_err(|e| { + format!( + "ffprobe failed on {} ({file_size} bytes): {e}", + output_path.display() + ) + })?; // Check container format. let format_name = &probe.format.format_name; From 921ca8dd3a097049692dae1995e2de858b857d52 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 17:09:06 +0000 Subject: [PATCH 23/69] fix(nodes): eagerly init Vulkan device to prevent empty output on fast pipelines The Vulkan Video H.264 encoder lazily initialised the VulkanDevice inside the blocking encode task on the first frame. On GPUs where device creation takes ~500 ms (common on CI runners), short pipelines such as colorbars (30 frames in ~12 ms) would close the input stream before the encoder was ready, resulting in zero encoded packets and an empty HTTP response. Move device initialisation to a dedicated spawn_blocking call that completes before the encode loop starts. The BytesEncoder is still created lazily on the first frame (to know the resolution), but the expensive Vulkan instance/adapter/device setup is already done. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vulkan_video.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs index df516eff..4b156450 100644 --- a/crates/nodes/src/video/vulkan_video.rs +++ b/crates/nodes/src/video/vulkan_video.rs @@ -491,13 +491,28 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { let (result_tx, mut result_rx) = mpsc::channel::>(get_codec_channel_capacity()); + // ── Pre-initialise Vulkan device ───────────────────────────────── + // Eagerly create the Vulkan device so the blocking encode task can + // start processing frames immediately. Without this, device + // creation (~500 ms on some GPUs) blocks the encode loop and + // causes short/fast pipelines (e.g. colorbars with 30 frames) to + // produce zero output — the input stream closes before the encoder + // is ready. + let pre_init_device = tokio::task::spawn_blocking(|| init_vulkan_encode_device(None)) + .await + .map_err(|e| { + StreamKitError::Runtime(format!("Vulkan device init task panicked: {e}")) + })? + .map_err(StreamKitError::Runtime)?; + // ── Blocking encode task ───────────────────────────────────────── let config = self.config.clone(); let encode_task = tokio::task::spawn_blocking(move || { - // Encoder and device are lazily initialised on the first frame - // so we know the actual resolution. + // The BytesEncoder is lazily created on the first frame (so we + // know the actual resolution), but the Vulkan device is already + // initialised above to avoid blocking frame reception. let mut encoder: Option = None; - let mut device: Option> = None; + let mut device: Option> = Some(pre_init_device); let mut current_dimensions: Option<(u32, u32)> = None; while let Some((frame, metadata)) = encode_rx.blocking_recv() { From 26ba11d96a20875744f6ebf41bb28f69ff8d0c99 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 17:09:18 +0000 Subject: [PATCH 24/69] fix(nodes): eagerly init Vulkan device to prevent empty output on fast pipelines The Vulkan Video H.264 encoder lazily initialised the VulkanDevice inside the blocking encode task on the first frame. On GPUs where device creation takes ~500 ms (common on CI runners), short pipelines such as colorbars (30 frames in ~12 ms) would close the input stream before the encoder was ready, resulting in zero encoded packets and an empty HTTP response. Move device initialisation to a dedicated spawn_blocking call that completes before the encode loop starts. The BytesEncoder is still created lazily on the first frame (to know the resolution), but the expensive Vulkan instance/adapter/device setup is already done. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vulkan_video.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs index 4b156450..8e3195af 100644 --- a/crates/nodes/src/video/vulkan_video.rs +++ b/crates/nodes/src/video/vulkan_video.rs @@ -500,9 +500,7 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { // is ready. let pre_init_device = tokio::task::spawn_blocking(|| init_vulkan_encode_device(None)) .await - .map_err(|e| { - StreamKitError::Runtime(format!("Vulkan device init task panicked: {e}")) - })? + .map_err(|e| StreamKitError::Runtime(format!("Vulkan device init task panicked: {e}")))? .map_err(StreamKitError::Runtime)?; // ── Blocking encode task ───────────────────────────────────────── From b2e4a70587bbecc0a74bbda96a751ec9985d35b3 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 17:27:54 +0000 Subject: [PATCH 25/69] fix(nodes): add panic detection and lifecycle tracing to codec forward loop - Log which select branch fires in codec_forward_loop (drain path) - Detect and log panics from codec tasks instead of silently swallowing - Track frames_encoded count in Vulkan encoder task - Increase GPU CI server log capture from 100 to 500 lines - Enable debug logging for codec/vulkan modules in GPU CI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- .github/workflows/e2e.yml | 6 +++--- crates/nodes/src/codec_utils.rs | 12 +++++++++++- crates/nodes/src/video/vulkan_video.rs | 4 ++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ca4e4b8c..49af14cc 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -154,7 +154,7 @@ jobs: # Use a non-default port to avoid conflicts with any persistent skit # instance that may be running on the self-hosted runner. SKIT_PORT=4546 - SK_SERVER__ADDRESS=127.0.0.1:${SKIT_PORT} ./target/debug/skit > /tmp/skit-gpu.log 2>&1 & + RUST_LOG=info,streamkit_nodes::codec_utils=debug,streamkit_nodes::video::vulkan_video=debug,streamkit_nodes::core::bytes_output=debug SK_SERVER__ADDRESS=127.0.0.1:${SKIT_PORT} ./target/debug/skit > /tmp/skit-gpu.log 2>&1 & echo $! > /tmp/skit-gpu.pid # Wait for the server to be healthy HEALTHY=0 @@ -180,8 +180,8 @@ jobs: if: always() run: | if [ -f /tmp/skit-gpu.log ]; then - echo "=== skit server logs (last 100 lines) ===" - tail -100 /tmp/skit-gpu.log + echo "=== skit server logs (last 500 lines) ===" + tail -500 /tmp/skit-gpu.log fi - name: Stop skit server diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index 7ed0cc8d..a788ca07 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -96,10 +96,13 @@ pub async fn codec_forward_loop( } } _ = &mut *input_task => { + tracing::debug!("{label} input task completed, draining codec results"); drop(codec_tx); + let mut drained = 0u64; while let Some(maybe_result) = result_rx.recv().await { match maybe_result { Ok(item) => { + drained += 1; if forward_one(to_packet(item), context, counter, stats).await { break; } @@ -107,11 +110,18 @@ pub async fn codec_forward_loop( Err(err) => handle_error(&err, counter, stats, label), } } + tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); break; } } } codec_task.abort(); - let _ = codec_task.await; + match codec_task.await { + Ok(()) => {}, + Err(e) if e.is_panic() => { + tracing::error!("{label} codec task panicked: {e:?}"); + }, + Err(_) => {}, + } } diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs index 8e3195af..18301d6a 100644 --- a/crates/nodes/src/video/vulkan_video.rs +++ b/crates/nodes/src/video/vulkan_video.rs @@ -512,6 +512,7 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { let mut encoder: Option = None; let mut device: Option> = Some(pre_init_device); let mut current_dimensions: Option<(u32, u32)> = None; + let mut frames_encoded: u64 = 0; while let Some((frame, metadata)) = encode_rx.blocking_recv() { if result_tx.is_closed() { @@ -617,6 +618,7 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { match result { Ok(encoded_chunk) => { + frames_encoded += 1; // Always propagate the keyframe flag, even when // the input had no metadata. Without this, // downstream RTMP/MoQ transport cannot detect @@ -639,6 +641,7 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { metadata: out_meta, }; if result_tx.blocking_send(Ok(output)).is_err() { + tracing::debug!("VulkanVideoH264EncoderNode result channel closed after {frames_encoded} frame(s)"); return; } }, @@ -656,6 +659,7 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { // adds flush(), it should be called here — matching the // decoder's flush at line ~245 and the pattern in // encoder_trait::spawn_standard_encode_task. + tracing::info!("VulkanVideoH264EncoderNode encode task finished after {frames_encoded} frame(s)"); }); // ── State transition ───────────────────────────────────────────── From bc74ab61879ffb538d2742fd0e0c6e364e1a0306 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 17:28:05 +0000 Subject: [PATCH 26/69] fix(nodes): add panic detection and lifecycle tracing to codec forward loop - Log which select branch fires in codec_forward_loop (drain path) - Detect and log panics from codec tasks instead of silently swallowing - Track frames_encoded count in Vulkan encoder task - Increase GPU CI server log capture from 100 to 500 lines - Enable debug logging for codec/vulkan modules in GPU CI Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vulkan_video.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs index 18301d6a..011f7dd3 100644 --- a/crates/nodes/src/video/vulkan_video.rs +++ b/crates/nodes/src/video/vulkan_video.rs @@ -659,7 +659,9 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { // adds flush(), it should be called here — matching the // decoder's flush at line ~245 and the pattern in // encoder_trait::spawn_standard_encode_task. - tracing::info!("VulkanVideoH264EncoderNode encode task finished after {frames_encoded} frame(s)"); + tracing::info!( + "VulkanVideoH264EncoderNode encode task finished after {frames_encoded} frame(s)" + ); }); // ── State transition ───────────────────────────────────────────── From ef994ec50118162a711884f3735f37e882ae2b5b Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 18:03:15 +0000 Subject: [PATCH 27/69] fix(nodes): force first Vulkan Video H.264 frame as IDR keyframe The MP4 muxer gates all video packets until it sees the first keyframe. The colorbars source does not set metadata.keyframe, so force_keyframe defaulted to false for every frame. Without an explicit IDR request the Vulkan Video encoder may not mark the first frame as a keyframe, causing the muxer to skip all 30 packets and produce an empty output. Also fix clippy lint: collapse identical Ok(())/Err(_) match arms in codec_forward_loop's codec-task await. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/codec_utils.rs | 3 +-- crates/nodes/src/video/vulkan_video.rs | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index a788ca07..b81596fc 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -118,10 +118,9 @@ pub async fn codec_forward_loop( codec_task.abort(); match codec_task.await { - Ok(()) => {}, Err(e) if e.is_panic() => { tracing::error!("{label} codec task panicked: {e:?}"); }, - Err(_) => {}, + _ => {}, } } diff --git a/crates/nodes/src/video/vulkan_video.rs b/crates/nodes/src/video/vulkan_video.rs index 011f7dd3..fe347332 100644 --- a/crates/nodes/src/video/vulkan_video.rs +++ b/crates/nodes/src/video/vulkan_video.rs @@ -601,7 +601,11 @@ impl ProcessorNode for VulkanVideoH264EncoderNode { }, }; - let force_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); + // The first frame MUST be an IDR so that downstream + // muxers (MP4 in particular gates on the first keyframe) + // and players can initialise correctly. + let force_keyframe = frames_encoded == 0 + || metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); let input_frame = vk_video::InputFrame { data: vk_video::RawFrameData { From fbacbbbd1d288bcc0695f732308a2deba7dc70a1 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 18:58:56 +0000 Subject: [PATCH 28/69] fix(nodes): add diagnostic tracing for pipeline shutdown race condition Add targeted tracing to identify why BytesOutputNode exits before receiving data from the MP4 muxer: - recv_with_cancellation: distinguish cancellation-token vs channel-close - graph_builder: log when each node task completes (success or error) - mp4 muxer: log keyframe gate decisions (first keyframe seen vs skip) Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/core/src/node.rs | 18 ++++++++++++++++-- crates/engine/src/graph_builder.rs | 14 ++++++++++++++ crates/nodes/src/containers/mp4.rs | 4 ++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/crates/core/src/node.rs b/crates/core/src/node.rs index df438925..61bd8347 100644 --- a/crates/core/src/node.rs +++ b/crates/core/src/node.rs @@ -356,8 +356,22 @@ impl NodeContext { pub async fn recv_with_cancellation(&self, rx: &mut mpsc::Receiver) -> Option { if let Some(token) = &self.cancellation_token { tokio::select! { - () = token.cancelled() => None, - packet = rx.recv() => packet, + () = token.cancelled() => { + tracing::debug!( + node = %self.output_sender.node_name(), + "recv_with_cancellation: cancelled by token" + ); + None + } + packet = rx.recv() => { + if packet.is_none() { + tracing::debug!( + node = %self.output_sender.node_name(), + "recv_with_cancellation: input channel closed" + ); + } + packet + } } } else { rx.recv().await diff --git a/crates/engine/src/graph_builder.rs b/crates/engine/src/graph_builder.rs index a3c542a6..c888ef09 100644 --- a/crates/engine/src/graph_builder.rs +++ b/crates/engine/src/graph_builder.rs @@ -400,6 +400,20 @@ pub async fn wire_and_spawn_graph( let result = node.run(context).await; let duration = start_time.elapsed(); + match &result { + Ok(()) => tracing::debug!( + node_id = %name, + ?duration, + "node task completed successfully", + ), + Err(e) => tracing::warn!( + node_id = %name, + ?duration, + error = %e, + "node task failed", + ), + } + let meter = global::meter("skit_engine"); let histogram = meter .f64_histogram("node.execution.duration") diff --git a/crates/nodes/src/containers/mp4.rs b/crates/nodes/src/containers/mp4.rs index 3e6c6701..7638073e 100644 --- a/crates/nodes/src/containers/mp4.rs +++ b/crates/nodes/src/containers/mp4.rs @@ -1166,8 +1166,12 @@ async fn run_stream_mode( if !video_keyframe_seen { if is_keyframe { + tracing::debug!("Mp4MuxerNode: first video keyframe received"); video_keyframe_seen = true; } else { + tracing::debug!( + "Mp4MuxerNode: skipping non-keyframe video (waiting for first keyframe, is_keyframe={is_keyframe})" + ); continue; } } From 6b9b100a13f6eb4c576220d797848d04935532e0 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:02:42 +0000 Subject: [PATCH 29/69] fix(nodes): await codec task before draining to prevent shutdown race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The codec_forward_loop drain phase previously interleaved with the (potentially slow) blocking encode task. On fast pipelines the drain could take 100+ ms while downstream nodes (MP4 muxer, BytesOutputNode) processed and closed their channels, resulting in zero-byte output. Restructure the drain so that we: 1. Break out of the select loop when the input task completes. 2. Await the codec (blocking) task to completion — all results are now buffered in result_rx. 3. Drain the fully-buffered results in a tight loop, forwarding them downstream before any channel can close. This eliminates the race window between result forwarding and downstream shutdown. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/codec_utils.rs | 41 ++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index b81596fc..7c93764b 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -69,6 +69,8 @@ pub async fn codec_forward_loop( tracing::warn!("{label} codec error: {err}"); } + let mut drain_pending = false; + loop { tokio::select! { maybe_result = result_rx.recv() => { @@ -96,8 +98,26 @@ pub async fn codec_forward_loop( } } _ = &mut *input_task => { - tracing::debug!("{label} input task completed, draining codec results"); + tracing::debug!("{label} input task completed, starting drain"); drop(codec_tx); + drain_pending = true; + break; + } + } + } + + if drain_pending { + // Wait for the codec task to finish producing all results before + // draining. Without this, the drain loop interleaves with the + // (potentially slow) blocking encode task, and downstream nodes + // may close their channels before all results are forwarded — + // causing zero-byte output on fast pipelines. + tracing::debug!("{label} waiting for codec task to finish before drain"); + match codec_task.await { + Err(e) if e.is_panic() => { + tracing::error!("{label} codec task panicked: {e:?}"); + }, + _ => { let mut drained = 0u64; while let Some(maybe_result) = result_rx.recv().await { match maybe_result { @@ -111,16 +131,15 @@ pub async fn codec_forward_loop( } } tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); - break; - } + }, + } + } else { + codec_task.abort(); + match codec_task.await { + Err(e) if e.is_panic() => { + tracing::error!("{label} codec task panicked: {e:?}"); + }, + _ => {}, } - } - - codec_task.abort(); - match codec_task.await { - Err(e) if e.is_panic() => { - tracing::error!("{label} codec task panicked: {e:?}"); - }, - _ => {}, } } From e4aa91cb1619e7405eb0a7b61e77801d36c52c60 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:02:53 +0000 Subject: [PATCH 30/69] style: format codec_utils.rs Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/codec_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index 7c93764b..aec7a7a3 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -126,7 +126,7 @@ pub async fn codec_forward_loop( if forward_one(to_packet(item), context, counter, stats).await { break; } - } + }, Err(err) => handle_error(&err, counter, stats, label), } } From 0ef7854e6f60139bcfe8b7c57a86983fd31ffaa8 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:08:14 +0000 Subject: [PATCH 31/69] fix(nodes): resolve clippy cognitive_complexity in codec_utils and mp4 - codec_utils: extract finish_codec_task helper to reduce nesting - mp4: flatten keyframe gate logic to remove nested if/else Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/codec_utils.rs | 55 ++++++++++++++---------------- crates/nodes/src/containers/mp4.rs | 17 +++++---- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index aec7a7a3..4886c166 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -16,6 +16,18 @@ use streamkit_core::types::Packet; use streamkit_core::NodeContext; use tokio::sync::mpsc; +/// Await a codec task and log if it panicked. Returns `true` when the +/// task panicked (caller should skip draining). +async fn finish_codec_task(codec_task: tokio::task::JoinHandle<()>, label: &str) -> bool { + match codec_task.await { + Err(e) if e.is_panic() => { + tracing::error!("{label} codec task panicked: {e:?}"); + true + }, + _ => false, + } +} + /// Shared select-loop that forwards codec results to the output sender. /// /// Handles three concurrent events: @@ -87,10 +99,6 @@ pub async fn codec_forward_loop( Some(control_msg) = context.control_rx.recv() => { if matches!(control_msg, streamkit_core::control::NodeControlMessage::Shutdown) { tracing::info!("{label} received shutdown signal"); - // NOTE: Dropping codec_tx first signals the codec thread to - // exit/flush, then aborting ensures it doesn't linger. - // Because we break out here, flushed results are never sent - // downstream. Data loss on explicit shutdown is acceptable. input_task.abort(); drop(codec_tx); codec_task.abort(); @@ -113,33 +121,22 @@ pub async fn codec_forward_loop( // may close their channels before all results are forwarded — // causing zero-byte output on fast pipelines. tracing::debug!("{label} waiting for codec task to finish before drain"); - match codec_task.await { - Err(e) if e.is_panic() => { - tracing::error!("{label} codec task panicked: {e:?}"); - }, - _ => { - let mut drained = 0u64; - while let Some(maybe_result) = result_rx.recv().await { - match maybe_result { - Ok(item) => { - drained += 1; - if forward_one(to_packet(item), context, counter, stats).await { - break; - } - }, - Err(err) => handle_error(&err, counter, stats, label), - } + if !finish_codec_task(codec_task, label).await { + let mut drained = 0u64; + while let Some(maybe_result) = result_rx.recv().await { + match maybe_result { + Ok(item) => { + drained += 1; + if forward_one(to_packet(item), context, counter, stats).await { + break; + } + }, + Err(err) => handle_error(&err, counter, stats, label), } - tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); - }, + } + tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); } } else { - codec_task.abort(); - match codec_task.await { - Err(e) if e.is_panic() => { - tracing::error!("{label} codec task panicked: {e:?}"); - }, - _ => {}, - } + finish_codec_task(codec_task, label).await; } } diff --git a/crates/nodes/src/containers/mp4.rs b/crates/nodes/src/containers/mp4.rs index 7638073e..1e5ab631 100644 --- a/crates/nodes/src/containers/mp4.rs +++ b/crates/nodes/src/containers/mp4.rs @@ -1164,16 +1164,15 @@ async fn run_stream_mode( MuxFrame::Video(data, metadata) => { let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); + if !video_keyframe_seen && !is_keyframe { + tracing::debug!( + "Mp4MuxerNode: skipping non-keyframe video (waiting for first keyframe)" + ); + continue; + } if !video_keyframe_seen { - if is_keyframe { - tracing::debug!("Mp4MuxerNode: first video keyframe received"); - video_keyframe_seen = true; - } else { - tracing::debug!( - "Mp4MuxerNode: skipping non-keyframe video (waiting for first keyframe, is_keyframe={is_keyframe})" - ); - continue; - } + tracing::debug!("Mp4MuxerNode: first video keyframe received"); + video_keyframe_seen = true; } packet_count += 1; From 83c0ecbc261e0cd49003ac74551aacdccb62b44b Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:12:39 +0000 Subject: [PATCH 32/69] refactor(nodes): extract accumulate_video_sample to fix mp4 cognitive_complexity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract video frame accumulation logic (Annex B → AVCC conversion, sample entry tracking, duration calculation) into a standalone helper to bring run_stream_mode under the cognitive_complexity threshold. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/containers/mp4.rs | 114 +++++++++++++++++------------ 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/crates/nodes/src/containers/mp4.rs b/crates/nodes/src/containers/mp4.rs index 1e5ab631..d8a32272 100644 --- a/crates/nodes/src/containers/mp4.rs +++ b/crates/nodes/src/containers/mp4.rs @@ -1098,6 +1098,65 @@ fn partition_samples_by_track( // Stream (fMP4) mode // --------------------------------------------------------------------------- +/// Accumulate a single video frame into the pending segment state. +/// +/// Handles Annex B → AVCC conversion for H.264, sample-entry tracking, +/// and duration calculation. +#[allow(clippy::too_many_arguments)] +fn accumulate_video_sample( + data: &Bytes, + metadata: Option<&PacketMetadata>, + is_keyframe: bool, + is_h264: bool, + config: &Mp4MuxerConfig, + video_timescale: NonZeroU32, + video_sample_entry: &mut SampleEntry, + seg: &mut Fmp4SegmentState, +) { + // Convert Annex B → AVCC for H.264 streams so the mdat + // contains length-prefixed NAL units matching the avc1 box. + let data = if is_h264 { + let conv = convert_annexb_to_avcc(data); + // On the first keyframe, update the sample entry with + // real SPS/PPS so the init segment (moov) describes the + // actual stream parameters instead of placeholders. + if !conv.sps_list.is_empty() && !seg.video_sample_entry_sent { + *video_sample_entry = rebuild_avc1_entry_from_params( + config.video_width, + config.video_height, + conv.sps_list, + conv.pps_list, + ); + } + conv.data + } else { + data.clone() + }; + + let duration_us = + metadata.as_ref().and_then(|m| m.duration_us).unwrap_or(DEFAULT_VIDEO_FRAME_DURATION_US); + let duration_ticks = us_to_ticks(duration_us, video_timescale.get()); + + let data_size = data.len(); + let entry = if seg.video_sample_entry_sent { + None + } else { + seg.video_sample_entry_sent = true; + Some(video_sample_entry.clone()) + }; + seg.pending_samples.push(Sample { + track_kind: TrackKind::Video, + timescale: video_timescale, + sample_entry: entry, + duration: duration_ticks, + keyframe: is_keyframe, + composition_time_offset: None, + data_offset: 0, // placeholder; partition_samples_by_track recomputes + data_size, + }); + seg.pending_payloads.push(data); +} + /// Run the muxer in fragmented MP4 (fMP4) streaming mode. /// /// Each batch of samples is turned into a media segment (moof + mdat) and @@ -1177,51 +1236,16 @@ async fn run_stream_mode( packet_count += 1; stats_tracker.received(); - - // Convert Annex B → AVCC for H.264 streams so the mdat - // contains length-prefixed NAL units matching the avc1 box. - let data = if is_h264 { - let conv = convert_annexb_to_avcc(&data); - // On the first keyframe, update the sample entry with - // real SPS/PPS so the init segment (moov) describes the - // actual stream parameters instead of placeholders. - if !conv.sps_list.is_empty() && !seg.video_sample_entry_sent { - video_sample_entry = rebuild_avc1_entry_from_params( - session.config.video_width, - session.config.video_height, - conv.sps_list, - conv.pps_list, - ); - } - conv.data - } else { - data - }; - - let duration_us = metadata - .as_ref() - .and_then(|m| m.duration_us) - .unwrap_or(DEFAULT_VIDEO_FRAME_DURATION_US); - let duration_ticks = us_to_ticks(duration_us, video_timescale.get()); - - let data_size = data.len(); - let entry = if seg.video_sample_entry_sent { - None - } else { - seg.video_sample_entry_sent = true; - Some(video_sample_entry.clone()) - }; - seg.pending_samples.push(Sample { - track_kind: TrackKind::Video, - timescale: video_timescale, - sample_entry: entry, - duration: duration_ticks, - keyframe: is_keyframe, - composition_time_offset: None, - data_offset: 0, // placeholder; partition_samples_by_track recomputes - data_size, - }); - seg.pending_payloads.push(data); + accumulate_video_sample( + &data, + metadata.as_ref(), + is_keyframe, + is_h264, + session.config, + video_timescale, + &mut video_sample_entry, + &mut seg, + ); }, MuxFrame::Audio(data, metadata) => { packet_count += 1; From f99fbdbb2c7de45699b100b247fe3fba811134ba Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:16:00 +0000 Subject: [PATCH 33/69] refactor(nodes): extract accumulate_audio_sample to further reduce mp4 complexity Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/containers/mp4.rs | 67 +++++++++++++++++++----------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/crates/nodes/src/containers/mp4.rs b/crates/nodes/src/containers/mp4.rs index d8a32272..8dfce5b2 100644 --- a/crates/nodes/src/containers/mp4.rs +++ b/crates/nodes/src/containers/mp4.rs @@ -1157,6 +1157,40 @@ fn accumulate_video_sample( seg.pending_payloads.push(data); } +/// Accumulate a single audio frame into the pending segment state. +fn accumulate_audio_sample( + data: Bytes, + metadata: Option<&PacketMetadata>, + audio_codec: AudioCodec, + audio_timescale: NonZeroU32, + audio_sample_entry: &SampleEntry, + seg: &mut Fmp4SegmentState, +) { + let duration_us = metadata + .and_then(|m| m.duration_us) + .unwrap_or_else(|| audio_codec.default_frame_duration_us()); + let duration_ticks = us_to_ticks(duration_us, audio_timescale.get()); + + let data_size = data.len(); + let entry = if seg.audio_sample_entry_sent { + None + } else { + seg.audio_sample_entry_sent = true; + Some(audio_sample_entry.clone()) + }; + seg.pending_samples.push(Sample { + track_kind: TrackKind::Audio, + timescale: audio_timescale, + sample_entry: entry, + duration: duration_ticks, + keyframe: true, // audio frames are always keyframes + composition_time_offset: None, + data_offset: 0, // placeholder; partition_samples_by_track recomputes + data_size, + }); + seg.pending_payloads.push(data); +} + /// Run the muxer in fragmented MP4 (fMP4) streaming mode. /// /// Each batch of samples is turned into a media segment (moof + mdat) and @@ -1250,31 +1284,14 @@ async fn run_stream_mode( MuxFrame::Audio(data, metadata) => { packet_count += 1; stats_tracker.received(); - - let duration_us = metadata - .as_ref() - .and_then(|m| m.duration_us) - .unwrap_or_else(|| session.audio_codec.default_frame_duration_us()); - let duration_ticks = us_to_ticks(duration_us, audio_timescale.get()); - - let data_size = data.len(); - let entry = if seg.audio_sample_entry_sent { - None - } else { - seg.audio_sample_entry_sent = true; - Some(audio_sample_entry.clone()) - }; - seg.pending_samples.push(Sample { - track_kind: TrackKind::Audio, - timescale: audio_timescale, - sample_entry: entry, - duration: duration_ticks, - keyframe: true, // audio frames are always keyframes - composition_time_offset: None, - data_offset: 0, // placeholder; partition_samples_by_track recomputes - data_size, - }); - seg.pending_payloads.push(data); + accumulate_audio_sample( + data, + metadata.as_ref(), + session.audio_codec, + audio_timescale, + &audio_sample_entry, + &mut seg, + ); }, } From edeba3da1bd898f2d7931c168a90f904f0a69c20 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:22:40 +0000 Subject: [PATCH 34/69] fix(nodes): abort codec task before awaiting in non-drain path Fixes a potential deadlock: when the output channel closes (e.g. client disconnect), the select loop breaks with drain_pending=false. Without aborting the codec task first, it may be blocked on blocking_send() with a full result channel that nobody is draining, causing finish_codec_task to wait forever. Identified by Devin Review. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/codec_utils.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index 4886c166..c652a57a 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -137,6 +137,11 @@ pub async fn codec_forward_loop( tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); } } else { + // Abort before awaiting: the codec task may be blocked on + // `result_tx.blocking_send()` with a full channel since nobody + // is draining `result_rx` anymore (output closed or channel + // dropped). Without the abort this would deadlock. + codec_task.abort(); finish_codec_task(codec_task, label).await; } } From 4c119a83e7b94d9e3c766772a296f2a977c39631 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:26:15 +0000 Subject: [PATCH 35/69] refactor(nodes): extract check_video_keyframe_gate to further reduce mp4 complexity Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/containers/mp4.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/nodes/src/containers/mp4.rs b/crates/nodes/src/containers/mp4.rs index 8dfce5b2..cf0ad0f2 100644 --- a/crates/nodes/src/containers/mp4.rs +++ b/crates/nodes/src/containers/mp4.rs @@ -1191,6 +1191,23 @@ fn accumulate_audio_sample( seg.pending_payloads.push(data); } +/// Check the keyframe gate for video frames. +/// +/// Returns `true` if the frame should be processed, `false` if it should be +/// skipped (still waiting for the first keyframe). +fn check_video_keyframe_gate(is_keyframe: bool, video_keyframe_seen: &mut bool) -> bool { + if *video_keyframe_seen { + return true; + } + if !is_keyframe { + tracing::debug!("Mp4MuxerNode: skipping non-keyframe video (waiting for first keyframe)"); + return false; + } + tracing::debug!("Mp4MuxerNode: first video keyframe received"); + *video_keyframe_seen = true; + true +} + /// Run the muxer in fragmented MP4 (fMP4) streaming mode. /// /// Each batch of samples is turned into a media segment (moof + mdat) and @@ -1256,17 +1273,9 @@ async fn run_stream_mode( }, MuxFrame::Video(data, metadata) => { let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); - - if !video_keyframe_seen && !is_keyframe { - tracing::debug!( - "Mp4MuxerNode: skipping non-keyframe video (waiting for first keyframe)" - ); + if !check_video_keyframe_gate(is_keyframe, &mut video_keyframe_seen) { continue; } - if !video_keyframe_seen { - tracing::debug!("Mp4MuxerNode: first video keyframe received"); - video_keyframe_seen = true; - } packet_count += 1; stats_tracker.received(); From 434d7f0ccf45fe6967a9a46d1260a6a90268ccd6 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 19:54:50 +0000 Subject: [PATCH 36/69] fix(nodes): populate avcC chroma fields for High profile H.264 The shiguredo_mp4 library requires chroma_format, bit_depth_luma_minus8, and bit_depth_chroma_minus8 fields in the AvccBox for profiles other than Baseline (66), Main (77), and Extended (88). HW encoders like Vulkan Video typically produce High profile (100) H.264, causing 'Missing chroma_format field in avcC box' when the MP4 muxer tries to create the init segment. Set 4:2:0 chroma (1) and 8-bit depth (0) for non-Baseline/Main/Extended profiles, matching the NV12 format used by all HW encoder backends. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/containers/mp4.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/nodes/src/containers/mp4.rs b/crates/nodes/src/containers/mp4.rs index cf0ad0f2..80af6e3b 100644 --- a/crates/nodes/src/containers/mp4.rs +++ b/crates/nodes/src/containers/mp4.rs @@ -89,6 +89,14 @@ fn build_avc1_sample_entry(width: u16, height: u16, codec_private: Option<&[u8]> parse_avcc_codec_private, ); + // For High profile and above (anything other than Baseline 66, + // Main 77, Extended 88), the avcC box requires chroma_format and + // bit-depth fields. Default to 4:2:0 / 8-bit which matches NV12. + let needs_chroma_fields = !matches!(profile, 66 | 77 | 88); + let chroma_format = if needs_chroma_fields { Some(Uint::new(1)) } else { None }; + let bit_depth_luma_minus8 = if needs_chroma_fields { Some(Uint::new(0)) } else { None }; + let bit_depth_chroma_minus8 = if needs_chroma_fields { Some(Uint::new(0)) } else { None }; + SampleEntry::Avc1(Avc1Box { visual: VisualSampleEntryFields { data_reference_index: VisualSampleEntryFields::DEFAULT_DATA_REFERENCE_INDEX, @@ -107,9 +115,9 @@ fn build_avc1_sample_entry(width: u16, height: u16, codec_private: Option<&[u8]> length_size_minus_one: Uint::new(3), sps_list, pps_list, - chroma_format: None, - bit_depth_luma_minus8: None, - bit_depth_chroma_minus8: None, + chroma_format, + bit_depth_luma_minus8, + bit_depth_chroma_minus8, sps_ext_list: vec![], }, unknown_boxes: vec![], @@ -296,6 +304,15 @@ fn rebuild_avc1_entry_from_params( .filter(|sps| sps.len() >= 4) .map_or((66, 0, 31), |sps| (sps[1], sps[2], sps[3])); + // For High profile and above (anything other than Baseline 66, + // Main 77, Extended 88), the avcC box requires chroma_format and + // bit-depth fields. Default to 4:2:0 / 8-bit which matches NV12 + // input — the standard output of HW encoders. + let needs_chroma_fields = !matches!(profile, 66 | 77 | 88); + let chroma_format = if needs_chroma_fields { Some(Uint::new(1)) } else { None }; + let bit_depth_luma_minus8 = if needs_chroma_fields { Some(Uint::new(0)) } else { None }; + let bit_depth_chroma_minus8 = if needs_chroma_fields { Some(Uint::new(0)) } else { None }; + SampleEntry::Avc1(Avc1Box { visual: VisualSampleEntryFields { data_reference_index: VisualSampleEntryFields::DEFAULT_DATA_REFERENCE_INDEX, @@ -314,9 +331,9 @@ fn rebuild_avc1_entry_from_params( length_size_minus_one: Uint::new(3), sps_list, pps_list, - chroma_format: None, - bit_depth_luma_minus8: None, - bit_depth_chroma_minus8: None, + chroma_format, + bit_depth_luma_minus8, + bit_depth_chroma_minus8, sps_ext_list: vec![], }, unknown_boxes: vec![], From 992a05f72c022c2993ec9938b5d219e74f949e13 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 20:06:04 +0000 Subject: [PATCH 37/69] fix(nodes): drain results concurrently with codec task to prevent deadlock The previous fix (awaiting the codec task before draining) introduced a deadlock when the codec produces more results than the bounded channel capacity (32). The codec task blocks on blocking_send() waiting for space, but nobody is draining result_rx because we're waiting for the codec task to finish first. Fix by using tokio::select! with biased polling: drain results from result_rx (keeping the channel flowing) while simultaneously awaiting the codec task. Once the codec task finishes, result_tx is dropped and result_rx.recv() returns None, ending the drain loop naturally with all results forwarded. This fixes opus_mp4 and opus_roundtrip pipeline validation tests that were hanging because the OpusEncoder produces ~51 frames (exceeding the 32-capacity channel). Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/codec_utils.rs | 55 ++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index c652a57a..ff0187f9 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -115,27 +115,52 @@ pub async fn codec_forward_loop( } if drain_pending { - // Wait for the codec task to finish producing all results before - // draining. Without this, the drain loop interleaves with the - // (potentially slow) blocking encode task, and downstream nodes - // may close their channels before all results are forwarded — - // causing zero-byte output on fast pipelines. + // Drain results concurrently with the codec task. We must keep + // reading from `result_rx` while the codec task is still running + // because the codec task uses `blocking_send()` on a bounded + // channel (capacity 32). If we awaited the codec task first + // (without draining), the channel would fill up and the codec + // task would block forever — a deadlock. + // + // Once the codec task finishes, `result_tx` is dropped, so + // `result_rx.recv()` will eventually return `None` and we exit + // the drain loop naturally with all results forwarded. tracing::debug!("{label} waiting for codec task to finish before drain"); - if !finish_codec_task(codec_task, label).await { - let mut drained = 0u64; - while let Some(maybe_result) = result_rx.recv().await { - match maybe_result { - Ok(item) => { - drained += 1; - if forward_one(to_packet(item), context, counter, stats).await { + let mut codec_task = codec_task; + let mut codec_done = false; + let mut drained = 0u64; + loop { + tokio::select! { + biased; + // Drain results first (biased) to keep the channel + // flowing and unblock the codec task's blocking_send(). + maybe_result = result_rx.recv() => { + match maybe_result { + Some(Ok(item)) => { + drained += 1; + if forward_one(to_packet(item), context, counter, stats).await { + break; + } + } + Some(Err(err)) => handle_error(&err, counter, stats, label), + None => break, // channel closed — codec task dropped result_tx + } + } + res = &mut codec_task, if !codec_done => { + codec_done = true; + if let Err(e) = res { + if e.is_panic() { + tracing::error!("{label} codec task panicked: {e:?}"); break; } - }, - Err(err) => handle_error(&err, counter, stats, label), + } + // Codec task finished — result_tx is dropped. + // Continue draining any buffered results in result_rx + // until recv() returns None. } } - tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); } + tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); } else { // Abort before awaiting: the codec task may be blocked on // `result_tx.blocking_send()` with a full channel since nobody From 94c02723d967aaa8717ac117be17a18dcd016975 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sat, 11 Apr 2026 20:10:41 +0000 Subject: [PATCH 38/69] refactor(nodes): extract drain_codec_results to reduce cognitive complexity Extract the concurrent drain loop into a separate drain_codec_results() function to bring codec_forward_loop back under the clippy cognitive_complexity limit (50). Also adds Send + Sync bounds to the to_packet closure parameter to support the extracted async function. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/codec_utils.rs | 123 +++++++++++++++++--------------- 1 file changed, 67 insertions(+), 56 deletions(-) diff --git a/crates/nodes/src/codec_utils.rs b/crates/nodes/src/codec_utils.rs index ff0187f9..6bb65653 100644 --- a/crates/nodes/src/codec_utils.rs +++ b/crates/nodes/src/codec_utils.rs @@ -16,16 +16,70 @@ use streamkit_core::types::Packet; use streamkit_core::NodeContext; use tokio::sync::mpsc; -/// Await a codec task and log if it panicked. Returns `true` when the -/// task panicked (caller should skip draining). -async fn finish_codec_task(codec_task: tokio::task::JoinHandle<()>, label: &str) -> bool { - match codec_task.await { - Err(e) if e.is_panic() => { - tracing::error!("{label} codec task panicked: {e:?}"); - true - }, - _ => false, +/// Drain remaining codec results while concurrently awaiting the codec task. +/// +/// We must keep reading from `result_rx` while the codec task is still +/// running because the codec task uses `blocking_send()` on a bounded +/// channel (capacity 32). If we awaited the codec task first (without +/// draining), the channel would fill up and the codec task would block +/// forever — a deadlock. +/// +/// Once the codec task finishes, `result_tx` is dropped, so +/// `result_rx.recv()` will eventually return `None` and we exit the +/// drain loop naturally with all results forwarded. +async fn drain_codec_results Packet + Send + Sync>( + result_rx: &mut mpsc::Receiver>, + mut codec_task: tokio::task::JoinHandle<()>, + context: &mut NodeContext, + counter: &opentelemetry::metrics::Counter, + stats: &mut NodeStatsTracker, + to_packet: &F, + label: &str, +) { + let mut codec_done = false; + let mut drained = 0u64; + loop { + tokio::select! { + biased; + // Drain results first (biased) to keep the channel + // flowing and unblock the codec task's blocking_send(). + maybe_result = result_rx.recv() => { + match maybe_result { + Some(Ok(item)) => { + drained += 1; + counter.add(1, &[KeyValue::new("status", "ok")]); + stats.received(); + if context.output_sender.send("out", to_packet(item)).await.is_err() { + tracing::debug!("Output channel closed during drain"); + break; + } + stats.sent(); + stats.maybe_send(); + } + Some(Err(err)) => { + counter.add(1, &[KeyValue::new("status", "error")]); + stats.received(); + stats.errored(); + stats.maybe_send(); + tracing::warn!("{label} codec error: {err}"); + } + None => break, // channel closed — codec task dropped result_tx + } + } + res = &mut codec_task, if !codec_done => { + codec_done = true; + if let Err(e) = res { + if e.is_panic() { + tracing::error!("{label} codec task panicked: {e:?}"); + break; + } + } + // Codec task finished — result_tx is dropped. + // Continue draining any buffered results until recv() returns None. + } + } } + tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); } /// Shared select-loop that forwards codec results to the output sender. @@ -45,7 +99,7 @@ pub async fn codec_forward_loop( codec_tx: mpsc::Sender, counter: &opentelemetry::metrics::Counter, stats: &mut NodeStatsTracker, - to_packet: impl Fn(T) -> Packet, + to_packet: impl Fn(T) -> Packet + Send + Sync, label: &str, ) { /// Forwards a single successful codec result to the output sender. @@ -115,58 +169,15 @@ pub async fn codec_forward_loop( } if drain_pending { - // Drain results concurrently with the codec task. We must keep - // reading from `result_rx` while the codec task is still running - // because the codec task uses `blocking_send()` on a bounded - // channel (capacity 32). If we awaited the codec task first - // (without draining), the channel would fill up and the codec - // task would block forever — a deadlock. - // - // Once the codec task finishes, `result_tx` is dropped, so - // `result_rx.recv()` will eventually return `None` and we exit - // the drain loop naturally with all results forwarded. tracing::debug!("{label} waiting for codec task to finish before drain"); - let mut codec_task = codec_task; - let mut codec_done = false; - let mut drained = 0u64; - loop { - tokio::select! { - biased; - // Drain results first (biased) to keep the channel - // flowing and unblock the codec task's blocking_send(). - maybe_result = result_rx.recv() => { - match maybe_result { - Some(Ok(item)) => { - drained += 1; - if forward_one(to_packet(item), context, counter, stats).await { - break; - } - } - Some(Err(err)) => handle_error(&err, counter, stats, label), - None => break, // channel closed — codec task dropped result_tx - } - } - res = &mut codec_task, if !codec_done => { - codec_done = true; - if let Err(e) = res { - if e.is_panic() { - tracing::error!("{label} codec task panicked: {e:?}"); - break; - } - } - // Codec task finished — result_tx is dropped. - // Continue draining any buffered results in result_rx - // until recv() returns None. - } - } - } - tracing::debug!("{label} drain complete: forwarded {drained} result(s)"); + drain_codec_results(result_rx, codec_task, context, counter, stats, &to_packet, label) + .await; } else { // Abort before awaiting: the codec task may be blocked on // `result_tx.blocking_send()` with a full channel since nobody // is draining `result_rx` anymore (output closed or channel // dropped). Without the abort this would deadlock. codec_task.abort(); - finish_codec_task(codec_task, label).await; + let _ = codec_task.await; } } From 4a73ed8d235164186e931d5e53e8190d58f376df Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 07:42:59 +0000 Subject: [PATCH 39/69] fix(nodes): detect keyframe from VA-API encoder bitstream output The VA-API AV1 and H.264 encoders were cloning input metadata without updating the keyframe flag based on actual encoder output. This caused downstream consumers (MP4 muxer, RTMP/MoQ transport) to miss keyframes, particularly encoder-initiated periodic keyframes from the LowDelay prediction structure. Add bitstream-level keyframe detection: - AV1: parse OBU headers to find Frame OBU with frame_type == KEY_FRAME - H.264: scan Annex B start codes for IDR NAL unit type (5) Both encode() and flush_encoder() paths now set the keyframe flag from the actual encoded bitstream rather than blindly cloning input metadata. Also fix HwAccelMode serde rename_all from "lowercase" to "snake_case" so ForceHw serializes as "force_hw" (not "forcehw"). Include unit tests for all keyframe detection functions. Co-Authored-By: Claudio Costa --- crates/nodes/src/video/mod.rs | 2 +- crates/nodes/src/video/vaapi_av1.rs | 198 ++++++++++++++++++++++++++- crates/nodes/src/video/vaapi_h264.rs | 160 +++++++++++++++++++++- 3 files changed, 353 insertions(+), 7 deletions(-) diff --git a/crates/nodes/src/video/mod.rs b/crates/nodes/src/video/mod.rs index f3b2b6f3..f09174ce 100644 --- a/crates/nodes/src/video/mod.rs +++ b/crates/nodes/src/video/mod.rs @@ -82,7 +82,7 @@ pub const H264_CONTENT_TYPE: &str = "video/h264"; #[derive( Debug, Clone, Copy, Default, serde::Serialize, serde::Deserialize, schemars::JsonSchema, )] -#[serde(rename_all = "lowercase")] +#[serde(rename_all = "snake_case")] pub enum HwAccelMode { /// Auto-detect: attempt hardware acceleration. /// diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 9eccd219..db65bffb 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -1092,9 +1092,10 @@ impl StandardVideoEncoder for VaapiAv1Encoder { loop { match self.encoder.poll() { Ok(Some(coded)) => { + let out_meta = merge_av1_keyframe_metadata(metadata.clone(), &coded.bitstream); packets.push(EncodedPacket { data: Bytes::from(coded.bitstream), - metadata: metadata.clone(), + metadata: out_meta, }); }, Ok(None) => break, @@ -1112,8 +1113,11 @@ impl StandardVideoEncoder for VaapiAv1Encoder { loop { match self.encoder.poll() { Ok(Some(coded)) => { - packets - .push(EncodedPacket { data: Bytes::from(coded.bitstream), metadata: None }); + let out_meta = merge_av1_keyframe_metadata(None, &coded.bitstream); + packets.push(EncodedPacket { + data: Bytes::from(coded.bitstream), + metadata: out_meta, + }); }, Ok(None) => break, Err(e) => return Err(format!("VA-API AV1 encoder poll error: {e}")), @@ -1128,6 +1132,114 @@ impl StandardVideoEncoder for VaapiAv1Encoder { } } +// --------------------------------------------------------------------------- +// Keyframe detection +// --------------------------------------------------------------------------- + +/// Detect whether an AV1 bitstream produced by the encoder contains a +/// keyframe by scanning for a Frame OBU with `frame_type == KEY_FRAME`. +/// +/// AV1 OBU header format (§ 5.3.1): +/// byte 0: `obu_forbidden_bit(1) | obu_type(4) | extension_flag(1) | has_size_field(1) | reserved(1)` +/// +/// OBU types of interest: +/// - 6 = `OBU_FRAME` (contains frame header + tile group) +/// - 3 = `OBU_FRAME_HEADER` +/// +/// Frame header first byte (§ 5.9.2): +/// `show_existing_frame(1) | frame_type(2) | ...` +/// frame_type 0 = KEY_FRAME +fn av1_bitstream_is_keyframe(bitstream: &[u8]) -> bool { + let mut pos = 0; + while pos < bitstream.len() { + let header_byte = bitstream[pos]; + let obu_type = (header_byte >> 3) & 0x0F; + let extension_flag = (header_byte >> 2) & 1; + let has_size_field = (header_byte >> 1) & 1; + pos += 1; + + // Skip extension header if present. + if extension_flag == 1 { + if pos >= bitstream.len() { + break; + } + pos += 1; + } + + // Read OBU size (LEB128) if has_size_field is set. + let obu_size = if has_size_field == 1 { + let mut size: u64 = 0; + let mut shift = 0; + loop { + if pos >= bitstream.len() { + return false; + } + let b = bitstream[pos]; + pos += 1; + size |= u64::from(b & 0x7F) << shift; + if b & 0x80 == 0 { + break; + } + shift += 7; + if shift >= 56 { + return false; // malformed LEB128 + } + } + Some(size as usize) + } else { + None + }; + + // OBU_FRAME (6) or OBU_FRAME_HEADER (3): check frame_type. + if obu_type == 6 || obu_type == 3 { + if pos < bitstream.len() { + let frame_byte = bitstream[pos]; + let show_existing_frame = (frame_byte >> 7) & 1; + if show_existing_frame == 0 { + let frame_type = (frame_byte >> 5) & 0x03; + // frame_type 0 = KEY_FRAME + return frame_type == 0; + } + } + return false; + } + + // Skip over this OBU's payload. + if let Some(size) = obu_size { + pos += size; + } else { + // Without size field, we can't skip — assume rest is one OBU. + break; + } + } + false +} + +/// Build output metadata with the keyframe flag set from encoder output. +/// +/// Uses bitstream-level detection because cros-codecs' +/// `CodedBitstreamBuffer.metadata.force_keyframe` only reflects the +/// *caller's* request — not encoder-initiated periodic keyframes from +/// the `LowDelay` prediction structure. +fn merge_av1_keyframe_metadata( + metadata: Option, + bitstream: &[u8], +) -> Option { + let is_keyframe = av1_bitstream_is_keyframe(bitstream); + Some(match metadata { + Some(mut m) => { + m.keyframe = Some(is_keyframe); + m + }, + None => PacketMetadata { + timestamp_us: None, + duration_us: None, + sequence: None, + keyframe: Some(is_keyframe), + }, + }) +} + // --------------------------------------------------------------------------- // Registration // --------------------------------------------------------------------------- @@ -1923,4 +2035,84 @@ mod tests { "VA-API AV1 encoder should be registered" ); } + + // ── Keyframe detection unit tests ──────────────────────────────── + + #[test] + fn test_av1_keyframe_detection_key_frame() { + // Minimal AV1 bitstream: Temporal Delimiter OBU + Sequence Header + // OBU + Frame OBU with frame_type = KEY_FRAME (0). + // + // OBU header byte: obu_type(4) << 3 | has_size_field(1) << 1 + // Temporal Delimiter (type 2): 0b_0_0010_0_1_0 = 0x12 + // Sequence Header (type 1): 0b_0_0001_0_1_0 = 0x0A + // Frame OBU (type 6): 0b_0_0110_0_1_0 = 0x32 + + let bitstream: Vec = vec![ + 0x12, + 0x00, // Temporal Delimiter OBU, size=0 + 0x0A, + 0x01, + 0x00, // Sequence Header OBU, size=1, dummy payload + 0x32, + 0x02, // Frame OBU, size=2 + 0b_0_00_00000, // show_existing_frame=0, frame_type=0 (KEY_FRAME), ... + 0x00, // dummy tile data + ]; + assert!(av1_bitstream_is_keyframe(&bitstream)); + } + + #[test] + fn test_av1_keyframe_detection_inter_frame() { + // Frame OBU with frame_type = INTER_FRAME (1). + let bitstream: Vec = vec![ + 0x32, + 0x02, // Frame OBU, size=2 + 0b_0_01_00000, // show_existing_frame=0, frame_type=1 (INTER_FRAME) + 0x00, + ]; + assert!(!av1_bitstream_is_keyframe(&bitstream)); + } + + #[test] + fn test_av1_keyframe_detection_empty() { + assert!(!av1_bitstream_is_keyframe(&[])); + } + + #[test] + fn test_av1_keyframe_detection_show_existing_frame() { + // Frame OBU with show_existing_frame=1 — not a keyframe. + let bitstream: Vec = vec![ + 0x32, + 0x02, // Frame OBU, size=2 + 0b_1_00_00000, // show_existing_frame=1 + 0x00, + ]; + assert!(!av1_bitstream_is_keyframe(&bitstream)); + } + + #[test] + fn test_merge_av1_keyframe_metadata_with_existing() { + let meta = PacketMetadata { + timestamp_us: Some(42_000), + duration_us: Some(33_333), + sequence: Some(7), + keyframe: None, + }; + // KEY_FRAME bitstream + let bitstream: Vec = vec![0x32, 0x02, 0b_0_00_00000, 0x00]; + let result = merge_av1_keyframe_metadata(Some(meta), &bitstream).unwrap(); + assert_eq!(result.keyframe, Some(true)); + assert_eq!(result.timestamp_us, Some(42_000)); + assert_eq!(result.sequence, Some(7)); + } + + #[test] + fn test_merge_av1_keyframe_metadata_without_existing() { + // INTER_FRAME bitstream + let bitstream: Vec = vec![0x32, 0x02, 0b_0_01_00000, 0x00]; + let result = merge_av1_keyframe_metadata(None, &bitstream).unwrap(); + assert_eq!(result.keyframe, Some(false)); + assert!(result.timestamp_us.is_none()); + } } diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs index 7285fd2c..8b27a604 100644 --- a/crates/nodes/src/video/vaapi_h264.rs +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -762,9 +762,10 @@ impl StandardVideoEncoder for VaapiH264Encoder { loop { match self.encoder.poll() { Ok(Some(coded)) => { + let out_meta = merge_h264_keyframe_metadata(metadata.clone(), &coded.bitstream); packets.push(EncodedPacket { data: Bytes::from(coded.bitstream), - metadata: metadata.clone(), + metadata: out_meta, }); }, Ok(None) => break, @@ -782,8 +783,11 @@ impl StandardVideoEncoder for VaapiH264Encoder { loop { match self.encoder.poll() { Ok(Some(coded)) => { - packets - .push(EncodedPacket { data: Bytes::from(coded.bitstream), metadata: None }); + let out_meta = merge_h264_keyframe_metadata(None, &coded.bitstream); + packets.push(EncodedPacket { + data: Bytes::from(coded.bitstream), + metadata: out_meta, + }); }, Ok(None) => break, Err(e) => return Err(format!("VA-API H.264 encoder poll error: {e}")), @@ -798,6 +802,77 @@ impl StandardVideoEncoder for VaapiH264Encoder { } } +// --------------------------------------------------------------------------- +// Keyframe detection +// --------------------------------------------------------------------------- + +/// Detect whether an H.264 Annex B bitstream contains an IDR (keyframe) +/// NAL unit. +/// +/// Scans for Annex B start codes (`00 00 01` or `00 00 00 01`) and checks +/// whether any NAL unit has `nal_unit_type == 5` (IDR slice). This is the +/// standard way to identify keyframes in H.264 elementary streams. +fn h264_bitstream_is_idr(bitstream: &[u8]) -> bool { + let len = bitstream.len(); + let mut i = 0; + while i + 2 < len { + // Look for 3-byte start code 00 00 01. + if bitstream[i] == 0 && bitstream[i + 1] == 0 && bitstream[i + 2] == 1 { + let nal_pos = i + 3; + if nal_pos < len { + let nal_type = bitstream[nal_pos] & 0x1F; + if nal_type == 5 { + return true; // IDR slice + } + } + i = nal_pos; + // Also handle 4-byte start code 00 00 00 01. + } else if i + 3 < len + && bitstream[i] == 0 + && bitstream[i + 1] == 0 + && bitstream[i + 2] == 0 + && bitstream[i + 3] == 1 + { + let nal_pos = i + 4; + if nal_pos < len { + let nal_type = bitstream[nal_pos] & 0x1F; + if nal_type == 5 { + return true; // IDR slice + } + } + i = nal_pos; + } else { + i += 1; + } + } + false +} + +/// Build output metadata with the keyframe flag set from encoder output. +/// +/// Uses bitstream-level detection because cros-codecs' +/// `CodedBitstreamBuffer.metadata.force_keyframe` only reflects the +/// *caller's* request — not encoder-initiated periodic keyframes from +/// the `LowDelay` prediction structure. +fn merge_h264_keyframe_metadata( + metadata: Option, + bitstream: &[u8], +) -> Option { + let is_keyframe = h264_bitstream_is_idr(bitstream); + Some(match metadata { + Some(mut m) => { + m.keyframe = Some(is_keyframe); + m + }, + None => PacketMetadata { + timestamp_us: None, + duration_us: None, + sequence: None, + keyframe: Some(is_keyframe), + }, + }) +} + // --------------------------------------------------------------------------- // Registration // --------------------------------------------------------------------------- @@ -1049,4 +1124,83 @@ mod tests { } } } + + // ── Keyframe detection unit tests ──────────────────────────────── + + #[test] + fn test_h264_idr_detection_with_4byte_start_code() { + // Annex B bitstream with 4-byte start code + IDR NAL (type 5). + // NAL header byte: forbidden_zero_bit(1)=0, nal_ref_idc(2)=3, nal_type(5)=5 + // → 0b_0_11_00101 = 0x65 + let bitstream: Vec = vec![ + 0x00, 0x00, 0x00, 0x01, 0x65, // IDR slice NAL + 0xAA, 0xBB, // dummy slice data + ]; + assert!(h264_bitstream_is_idr(&bitstream)); + } + + #[test] + fn test_h264_idr_detection_with_3byte_start_code() { + // Annex B bitstream with 3-byte start code + IDR NAL (type 5). + let bitstream: Vec = vec![ + 0x00, 0x00, 0x01, 0x65, // IDR slice NAL + 0xAA, // dummy data + ]; + assert!(h264_bitstream_is_idr(&bitstream)); + } + + #[test] + fn test_h264_non_idr_detection() { + // Non-IDR slice NAL (type 1). + // NAL header: nal_ref_idc=2, nal_type=1 → 0b_0_10_00001 = 0x41 + let bitstream: Vec = vec![ + 0x00, 0x00, 0x00, 0x01, 0x41, // Non-IDR slice NAL + 0xCC, + ]; + assert!(!h264_bitstream_is_idr(&bitstream)); + } + + #[test] + fn test_h264_idr_with_sps_pps_prefix() { + // Typical encoder output: SPS (type 7) + PPS (type 8) + IDR (type 5). + let bitstream: Vec = vec![ + 0x00, 0x00, 0x00, 0x01, 0x67, // SPS NAL (type 7) + 0x42, 0x00, 0x1E, // dummy SPS data + 0x00, 0x00, 0x00, 0x01, 0x68, // PPS NAL (type 8) + 0xCE, 0x38, 0x80, // dummy PPS data + 0x00, 0x00, 0x00, 0x01, 0x65, // IDR slice NAL (type 5) + 0x88, 0x80, // dummy slice data + ]; + assert!(h264_bitstream_is_idr(&bitstream)); + } + + #[test] + fn test_h264_idr_detection_empty() { + assert!(!h264_bitstream_is_idr(&[])); + } + + #[test] + fn test_merge_h264_keyframe_metadata_with_existing() { + let meta = PacketMetadata { + timestamp_us: Some(100_000), + duration_us: Some(33_333), + sequence: Some(3), + keyframe: None, + }; + // IDR bitstream + let bitstream: Vec = vec![0x00, 0x00, 0x00, 0x01, 0x65, 0xAA]; + let result = merge_h264_keyframe_metadata(Some(meta), &bitstream).unwrap(); + assert_eq!(result.keyframe, Some(true)); + assert_eq!(result.timestamp_us, Some(100_000)); + assert_eq!(result.sequence, Some(3)); + } + + #[test] + fn test_merge_h264_keyframe_metadata_without_existing() { + // Non-IDR bitstream + let bitstream: Vec = vec![0x00, 0x00, 0x00, 0x01, 0x41, 0xBB]; + let result = merge_h264_keyframe_metadata(None, &bitstream).unwrap(); + assert_eq!(result.keyframe, Some(false)); + assert!(result.timestamp_us.is_none()); + } } From 2e9f7357125a9bdb5209faa1e051e119b9198691 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 15:11:40 +0000 Subject: [PATCH 40/69] fix(nodes): construct VA-API encoder backend directly to satisfy trait bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `CrosVaapiAv1Encoder` and `CrosVaapiH264Encoder` type aliases use `Surface<()>` to bypass GBM buffer allocation, but `Surface<()>` does not implement the `VideoFrame` trait required by `new_vaapi()`. Replace `new_vaapi()` calls with direct `VaapiBackend::new()` + `new_av1()`/`new_h264()` construction — the same pattern used by cros-codecs' own tests — which avoids the `V: VideoFrame` constraint while preserving the GBM-free surface path. Also removes unused imports (GbmDevice, GbmExternalBufferDescriptor, ReadMapping, WriteMapping, CrosFourcc, write_nv12_to_mapping) that were flagged as warnings. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 28 +++++++++++++++-------- crates/nodes/src/video/vaapi_h264.rs | 34 +++++++++++++++++++--------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index db65bffb..fb2f12c3 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -51,6 +51,7 @@ use tokio::sync::mpsc; // cros-codecs high-level APIs. use cros_codecs::backend::vaapi::decoder::VaapiBackend as VaapiDecBackend; +use cros_codecs::backend::vaapi::encoder::VaapiBackend as VaapiEncBackend; use cros_codecs::codec::av1::parser::Profile as Av1Profile; use cros_codecs::decoder::stateless::av1::Av1; use cros_codecs::decoder::stateless::{DecodeError, StatelessDecoder, StatelessVideoDecoder}; @@ -61,11 +62,9 @@ use cros_codecs::encoder::{ FrameMetadata as CrosFrameMetadata, PredictionStructure, RateControl, Tunings, VideoEncoder, }; use cros_codecs::libva; -use cros_codecs::video_frame::gbm_video_frame::{ - GbmDevice, GbmExternalBufferDescriptor, GbmUsage, GbmVideoFrame, -}; -use cros_codecs::video_frame::{ReadMapping, VideoFrame as CrosVideoFrame, WriteMapping}; -use cros_codecs::{Fourcc as CrosFourcc, FrameLayout, PlaneLayout, Resolution as CrosResolution}; +use cros_codecs::video_frame::gbm_video_frame::{GbmUsage, GbmVideoFrame}; +use cros_codecs::video_frame::VideoFrame as CrosVideoFrame; +use cros_codecs::{FrameLayout, PlaneLayout, Resolution as CrosResolution}; use super::encoder_trait::{self, EncodedPacket, EncoderNodeRunner, StandardVideoEncoder}; use super::HwAccelMode; @@ -1011,15 +1010,24 @@ impl StandardVideoEncoder for VaapiAv1Encoder { }, }; - let encoder = CrosVaapiAv1Encoder::new_vaapi( + // Construct the VA-API encoder backend directly instead of using + // `new_vaapi()`, which requires `V: VideoFrame`. Our type alias + // uses `Surface<()>` (bypassing GBM allocation), so we replicate + // the profile mapping and backend construction inline. + let va_profile = libva::VAProfile::VAProfileAV1Profile0; + let coded_size = CrosResolution { width: coded_width, height: coded_height }; + let backend = VaapiEncBackend::new( Rc::clone(&display), - cros_config, + va_profile, nv12_fourcc(), - CrosResolution { width: coded_width, height: coded_height }, + coded_size, + libva::VA_RC_CQP, config.low_power, - BlockingMode::Blocking, ) - .map_err(|e| format!("failed to create VA-API AV1 encoder: {e}"))?; + .map_err(|e| format!("failed to create VA-API AV1 encoder backend: {e}"))?; + + let encoder = CrosVaapiAv1Encoder::new_av1(backend, cros_config, BlockingMode::Blocking) + .map_err(|e| format!("failed to create VA-API AV1 encoder: {e}"))?; tracing::info!( device = %path, diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs index 8b27a604..fabc59b2 100644 --- a/crates/nodes/src/video/vaapi_h264.rs +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -51,6 +51,7 @@ use tokio::sync::mpsc; // cros-codecs high-level APIs. use cros_codecs::backend::vaapi::decoder::VaapiBackend as VaapiDecBackend; +use cros_codecs::backend::vaapi::encoder::VaapiBackend as VaapiEncBackend; use cros_codecs::codec::h264::parser::Level as H264Level; use cros_codecs::codec::h264::parser::Profile as H264Profile; use cros_codecs::decoder::stateless::h264::H264; @@ -62,11 +63,9 @@ use cros_codecs::encoder::{ FrameMetadata as CrosFrameMetadata, PredictionStructure, RateControl, Tunings, VideoEncoder, }; use cros_codecs::libva; -use cros_codecs::video_frame::gbm_video_frame::{ - GbmDevice, GbmExternalBufferDescriptor, GbmUsage, GbmVideoFrame, -}; -use cros_codecs::video_frame::{ReadMapping, VideoFrame as CrosVideoFrame, WriteMapping}; -use cros_codecs::{Fourcc as CrosFourcc, FrameLayout, PlaneLayout, Resolution as CrosResolution}; +use cros_codecs::video_frame::gbm_video_frame::{GbmUsage, GbmVideoFrame}; +use cros_codecs::video_frame::VideoFrame as CrosVideoFrame; +use cros_codecs::{FrameLayout, PlaneLayout, Resolution as CrosResolution}; use super::encoder_trait::{self, EncodedPacket, EncoderNodeRunner, StandardVideoEncoder}; use super::HwAccelMode; @@ -76,7 +75,7 @@ use super::H264_CONTENT_TYPE; // I/O routines (VA surface upload, GBM mapping, render-device detection, etc.). use super::vaapi_av1::{ align_up_u32, nv12_fourcc, open_va_and_gbm, open_va_display, read_nv12_from_mapping, - write_nv12_to_mapping, write_nv12_to_va_surface, + write_nv12_to_va_surface, }; // --------------------------------------------------------------------------- @@ -681,15 +680,28 @@ impl StandardVideoEncoder for VaapiH264Encoder { }, }; - let encoder = CrosVaapiH264Encoder::new_vaapi( + // Construct the VA-API encoder backend directly instead of using + // `new_vaapi()`, which requires `V: VideoFrame`. Our type alias + // uses `Surface<()>` (bypassing GBM allocation), so we replicate + // the profile mapping and backend construction inline. + let va_profile = libva::VAProfile::VAProfileH264Main; + let bitrate_control = match cros_config.initial_tunings.rate_control { + RateControl::ConstantBitrate(_) => libva::VA_RC_CBR, + RateControl::ConstantQuality(_) => libva::VA_RC_CQP, + }; + let coded_size = CrosResolution { width: coded_width, height: coded_height }; + let backend = VaapiEncBackend::new( Rc::clone(&display), - cros_config, + va_profile, nv12_fourcc(), - CrosResolution { width: coded_width, height: coded_height }, + coded_size, + bitrate_control, low_power, - BlockingMode::Blocking, ) - .map_err(|e| format!("failed to create VA-API H.264 encoder: {e}"))?; + .map_err(|e| format!("failed to create VA-API H.264 encoder backend: {e}"))?; + + let encoder = CrosVaapiH264Encoder::new_h264(backend, cros_config, BlockingMode::Blocking) + .map_err(|e| format!("failed to create VA-API H.264 encoder: {e}"))?; tracing::info!( device = %path, From 5ffdc82df720523c44cf45572ad57afffd6d499d Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 15:14:22 +0000 Subject: [PATCH 41/69] fix(nodes): restore required imports removed in previous commit Restore GbmDevice, ReadMapping, WriteMapping, and CrosFourcc imports that are used by decoder and NV12 helper functions in vaapi_av1.rs. Only GbmExternalBufferDescriptor was truly unused. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index fb2f12c3..509d082a 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -62,9 +62,9 @@ use cros_codecs::encoder::{ FrameMetadata as CrosFrameMetadata, PredictionStructure, RateControl, Tunings, VideoEncoder, }; use cros_codecs::libva; -use cros_codecs::video_frame::gbm_video_frame::{GbmUsage, GbmVideoFrame}; -use cros_codecs::video_frame::VideoFrame as CrosVideoFrame; -use cros_codecs::{FrameLayout, PlaneLayout, Resolution as CrosResolution}; +use cros_codecs::video_frame::gbm_video_frame::{GbmDevice, GbmUsage, GbmVideoFrame}; +use cros_codecs::video_frame::{ReadMapping, VideoFrame as CrosVideoFrame, WriteMapping}; +use cros_codecs::{Fourcc as CrosFourcc, FrameLayout, PlaneLayout, Resolution as CrosResolution}; use super::encoder_trait::{self, EncodedPacket, EncoderNodeRunner, StandardVideoEncoder}; use super::HwAccelMode; From d3f84badbed3456f24fd909d5f090a22677f5388 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 15:45:00 +0000 Subject: [PATCH 42/69] fix(nodes): use GbmVideoFrame for VA-API encoders with runtime GBM fallback Replace Surface<()> type alias with GbmVideoFrame in both VA-API AV1 and H.264 encoders. This satisfies the VideoFrame trait bound required by StatelessEncoder::new_vaapi(), fixing the build with --features vaapi. At construction time, the encoder probes GBM buffer allocation with GBM_BO_USE_HW_VIDEO_ENCODER. If the driver does not support that flag (e.g. Mesa iris on Intel Tiger Lake with Mesa 23.x), it falls back to GBM_BO_USE_HW_VIDEO_DECODER which is universally supported and still produces a valid NV12 buffer the encoder can read. Also removes the now-unused open_va_display() and write_nv12_to_va_surface() helper functions, and the direct VaapiEncBackend import that was only needed for the old manual backend construction path. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 251 ++++++++++----------------- crates/nodes/src/video/vaapi_h264.rs | 139 +++++++++------ 2 files changed, 170 insertions(+), 220 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 509d082a..7b47f9e0 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -51,7 +51,6 @@ use tokio::sync::mpsc; // cros-codecs high-level APIs. use cros_codecs::backend::vaapi::decoder::VaapiBackend as VaapiDecBackend; -use cros_codecs::backend::vaapi::encoder::VaapiBackend as VaapiEncBackend; use cros_codecs::codec::av1::parser::Profile as Av1Profile; use cros_codecs::decoder::stateless::av1::Av1; use cros_codecs::decoder::stateless::{DecodeError, StatelessDecoder, StatelessVideoDecoder}; @@ -62,7 +61,9 @@ use cros_codecs::encoder::{ FrameMetadata as CrosFrameMetadata, PredictionStructure, RateControl, Tunings, VideoEncoder, }; use cros_codecs::libva; -use cros_codecs::video_frame::gbm_video_frame::{GbmDevice, GbmUsage, GbmVideoFrame}; +use cros_codecs::video_frame::gbm_video_frame::{ + GbmDevice, GbmExternalBufferDescriptor, GbmUsage, GbmVideoFrame, +}; use cros_codecs::video_frame::{ReadMapping, VideoFrame as CrosVideoFrame, WriteMapping}; use cros_codecs::{Fourcc as CrosFourcc, FrameLayout, PlaneLayout, Resolution as CrosResolution}; @@ -162,123 +163,6 @@ pub(super) fn open_va_and_gbm( Ok((display, gbm, path)) } -/// Open a VA display without a GBM device. -/// -/// Used by encoder paths that pass VA surfaces directly to the encoder, -/// bypassing GBM buffer allocation entirely. This avoids the -/// `GBM_BO_USE_HW_VIDEO_ENCODER` flag that Mesa's iris driver does not -/// support for NV12 on some hardware (e.g. Intel Tiger Lake). -pub(super) fn open_va_display( - render_device: Option<&String>, -) -> Result<(Rc, String), String> { - let path = resolve_render_device(render_device); - let display = libva::Display::open_drm_display(&path) - .map_err(|e| format!("failed to open VA display on {path}: {e}"))?; - Ok((display, path)) -} - -/// Write NV12 (or I420→NV12) data from a StreamKit [`VideoFrame`] into a VA -/// surface using the VA-API Image API. -/// -/// Uses `vaCreateImage` + `vaMapBuffer` to obtain a writable mapping, writes -/// NV12 data respecting the driver's internal pitches/offsets, then drops the -/// [`Image`] which flushes the data back via `vaPutImage`. -/// -/// Returns `(pitches, offsets)` — the per-plane stride and byte-offset arrays -/// from the `VAImage`, needed to build the [`FrameLayout`] for the encoder. -pub(super) fn write_nv12_to_va_surface( - display: &Rc, - surface: &libva::Surface<()>, - frame: &VideoFrame, -) -> Result<([usize; 2], [usize; 2]), String> { - let nv12_fourcc_val: u32 = nv12_fourcc().into(); - let image_fmts = display - .query_image_formats() - .map_err(|e| format!("failed to query VA image formats: {e}"))?; - let image_fmt = image_fmts - .into_iter() - .find(|f| f.fourcc == nv12_fourcc_val) - .ok_or("VA driver does not support NV12 image format")?; - - let mut image = libva::Image::create_from(surface, image_fmt, surface.size(), surface.size()) - .map_err(|e| format!("failed to create VA image for NV12 upload: {e}"))?; - - let va_image = *image.image(); - let y_pitch = va_image.pitches[0] as usize; - let uv_pitch = va_image.pitches[1] as usize; - let y_offset = va_image.offsets[0] as usize; - let uv_offset = va_image.offsets[1] as usize; - - let dest = image.as_mut(); - let src = frame.data.as_ref().as_ref(); - let w = frame.width as usize; - let h = frame.height as usize; - - match frame.pixel_format { - PixelFormat::Nv12 => { - // Y plane. - for row in 0..h { - let s = row * w; - let d = y_offset + row * y_pitch; - if s + w <= src.len() && d + w <= dest.len() { - dest[d..d + w].copy_from_slice(&src[s..s + w]); - } - } - // UV plane (already interleaved in NV12). - // Use ceiling division to handle odd dimensions, matching - // VideoLayout::packed which uses `(width + 1) / 2`. - let uv_h = (h + 1) / 2; - let chroma_row_bytes = ((w + 1) / 2) * 2; - let src_uv = &src[w * h..]; - for row in 0..uv_h { - let s = row * chroma_row_bytes; - let d = uv_offset + row * uv_pitch; - if s + chroma_row_bytes <= src_uv.len() && d + chroma_row_bytes <= dest.len() { - dest[d..d + chroma_row_bytes].copy_from_slice(&src_uv[s..s + chroma_row_bytes]); - } - } - }, - PixelFormat::I420 => { - // Y plane — same as NV12. - for row in 0..h { - let s = row * w; - let d = y_offset + row * y_pitch; - if s + w <= src.len() && d + w <= dest.len() { - dest[d..d + w].copy_from_slice(&src[s..s + w]); - } - } - // I420 → NV12: interleave U and V into a single UV plane. - // Use ceiling division to handle odd dimensions correctly, - // matching VideoLayout::packed and i420_to_nv12_buffer. - let uv_w = (w + 1) / 2; - let uv_h = (h + 1) / 2; - let u_start = w * h; - let v_start = u_start + uv_w * uv_h; - for row in 0..uv_h { - for col in 0..uv_w { - let u_idx = u_start + row * uv_w + col; - let v_idx = v_start + row * uv_w + col; - let d = uv_offset + row * uv_pitch + col * 2; - if u_idx < src.len() && v_idx < src.len() && d + 1 < dest.len() { - dest[d] = src[u_idx]; - dest[d + 1] = src[v_idx]; - } - } - } - }, - other => { - drop(image); - return Err(format!("write_nv12_to_va_surface: unsupported pixel format {other:?}")); - }, - } - - // Sync the surface before dropping the image (which calls vaPutImage). - surface.sync().map_err(|e| format!("VA surface sync failed: {e}"))?; - drop(image); - - Ok(([y_pitch, uv_pitch], [y_offset, uv_offset])) -} - /// Copy NV12 plane data from a GBM read-mapping into a flat `Vec` suitable /// for a packed StreamKit [`VideoFrame`]. /// @@ -962,14 +846,20 @@ impl EncoderNodeRunner for VaapiAv1EncoderNode { // Encoder — internal codec wrapper // --------------------------------------------------------------------------- -/// Type alias for the VA-API AV1 encoder using direct VA surfaces. +/// Type alias for the VA-API AV1 encoder using GBM-backed video frames. /// -/// Bypasses GBM buffer allocation entirely — see the H.264 encoder type alias -/// in `vaapi_h264.rs` for the full rationale. +/// The `GbmVideoFrame` handle satisfies the `VideoFrame` trait bound +/// required by `StatelessEncoder::new_vaapi()`. At runtime, GBM buffer +/// allocation uses `GBM_BO_USE_HW_VIDEO_ENCODER` when supported, and +/// falls back to `GBM_BO_USE_HW_VIDEO_DECODER` on drivers where the +/// encoder flag is unsupported (e.g. Mesa iris on Intel Tiger Lake). type CrosVaapiAv1Encoder = StatelessEncoder< cros_codecs::encoder::av1::AV1, - libva::Surface<()>, - cros_codecs::backend::vaapi::encoder::VaapiBackend<(), libva::Surface<()>>, + GbmVideoFrame, + cros_codecs::backend::vaapi::encoder::VaapiBackend< + GbmExternalBufferDescriptor, + libva::Surface, + >, >; /// Internal encoder state wrapping the cros-codecs `StatelessEncoder`. @@ -979,6 +869,12 @@ type CrosVaapiAv1Encoder = StatelessEncoder< struct VaapiAv1Encoder { encoder: CrosVaapiAv1Encoder, display: Rc, + gbm: Arc, + /// GBM buffer usage flag. Defaults to `Encode` (optimal tiling for the + /// encoder HW), but falls back to `Decode` on drivers where + /// `GBM_BO_USE_HW_VIDEO_ENCODER` is unsupported (e.g. Mesa iris on + /// Intel Tiger Lake with Mesa 23.x). + gbm_usage: GbmUsage, width: u32, height: u32, coded_width: u32, @@ -991,12 +887,40 @@ impl StandardVideoEncoder for VaapiAv1Encoder { const CODEC_NAME: &'static str = "VA-API AV1"; fn new_encoder(width: u32, height: u32, config: &Self::Config) -> Result { - let (display, path) = open_va_display(config.render_device.as_ref())?; + let (display, gbm, path) = open_va_and_gbm(config.render_device.as_ref())?; tracing::info!(device = %path, width, height, "VA-API AV1 encoder opening"); let coded_width = align_up_u32(width, AV1_SB_SIZE); let coded_height = align_up_u32(height, AV1_SB_SIZE); + // Probe GBM encoder buffer support. Some drivers (Mesa iris on + // Intel Tiger Lake with Mesa 23.x) do not support the + // GBM_BO_USE_HW_VIDEO_ENCODER flag for NV12. In that case, fall + // back to GBM_BO_USE_HW_VIDEO_DECODER which is universally + // supported and still produces a valid NV12 buffer the encoder + // can read. + let gbm_usage = { + let probe_res = CrosResolution { width: coded_width, height: coded_height }; + match Arc::clone(&gbm).new_frame( + nv12_fourcc(), + probe_res.clone(), + probe_res, + GbmUsage::Encode, + ) { + Ok(_) => { + tracing::debug!("GBM encoder buffer allocation OK"); + GbmUsage::Encode + }, + Err(_) => { + tracing::warn!( + "GBM_BO_USE_HW_VIDEO_ENCODER unsupported on this driver; \ + falling back to GBM_BO_USE_HW_VIDEO_DECODER for encoder input buffers" + ); + GbmUsage::Decode + }, + } + }; + let cros_config = CrosEncoderConfig { profile: Av1Profile::Profile0, bit_depth: cros_codecs::codec::av1::parser::BitDepth::Depth8, @@ -1010,24 +934,15 @@ impl StandardVideoEncoder for VaapiAv1Encoder { }, }; - // Construct the VA-API encoder backend directly instead of using - // `new_vaapi()`, which requires `V: VideoFrame`. Our type alias - // uses `Surface<()>` (bypassing GBM allocation), so we replicate - // the profile mapping and backend construction inline. - let va_profile = libva::VAProfile::VAProfileAV1Profile0; - let coded_size = CrosResolution { width: coded_width, height: coded_height }; - let backend = VaapiEncBackend::new( + let encoder = CrosVaapiAv1Encoder::new_vaapi( Rc::clone(&display), - va_profile, + cros_config, nv12_fourcc(), - coded_size, - libva::VA_RC_CQP, + CrosResolution { width: coded_width, height: coded_height }, config.low_power, + BlockingMode::Blocking, ) - .map_err(|e| format!("failed to create VA-API AV1 encoder backend: {e}"))?; - - let encoder = CrosVaapiAv1Encoder::new_av1(backend, cros_config, BlockingMode::Blocking) - .map_err(|e| format!("failed to create VA-API AV1 encoder: {e}"))?; + .map_err(|e| format!("failed to create VA-API AV1 encoder: {e}"))?; tracing::info!( device = %path, @@ -1036,10 +951,21 @@ impl StandardVideoEncoder for VaapiAv1Encoder { coded_width, coded_height, quality = config.quality, + gbm_usage = ?gbm_usage, "VA-API AV1 encoder created" ); - Ok(Self { encoder, display, width, height, coded_width, coded_height, frame_count: 0 }) + Ok(Self { + encoder, + display, + gbm, + gbm_usage, + width, + height, + coded_width, + coded_height, + frame_count: 0, + }) } fn encode( @@ -1053,36 +979,33 @@ impl StandardVideoEncoder for VaapiAv1Encoder { .into()); } - // Create a VA surface and upload NV12 data via the Image API. - // This bypasses GBM buffer allocation (GBM_BO_USE_HW_VIDEO_ENCODER), - // which Mesa's iris driver does not support for NV12 on all hardware. - let nv12_fourcc_val: u32 = nv12_fourcc().into(); - let mut surfaces = self - .display - .create_surfaces( - libva::VA_RT_FORMAT_YUV420, - Some(nv12_fourcc_val), - self.coded_width, - self.coded_height, - Some(libva::UsageHint::USAGE_HINT_ENCODER), - vec![()], - ) - .map_err(|e| format!("failed to create VA surface for encoding: {e}"))?; - let surface = - surfaces.pop().ok_or_else(|| "create_surfaces returned empty vec".to_string())?; - - // Write frame data into the VA surface. - let (pitches, offsets) = write_nv12_to_va_surface(&self.display, &surface, frame)?; + // Allocate a GBM frame and write NV12 data into it. + let visible_res = CrosResolution { width: self.width, height: self.height }; + let coded_res = CrosResolution { width: self.coded_width, height: self.coded_height }; + let mut gbm_frame = Arc::clone(&self.gbm) + .new_frame(nv12_fourcc(), visible_res, coded_res, self.gbm_usage.clone()) + .map_err(|e| format!("failed to allocate GBM frame for encoding: {e}"))?; + + // Write NV12 (or I420→NV12) data into the GBM buffer. + let pitches = gbm_frame.get_plane_pitch(); + { + let mapping = gbm_frame + .map_mut() + .map_err(|e| format!("failed to map GBM frame for writing: {e}"))?; + write_nv12_to_mapping(mapping.as_ref(), frame, &pitches)?; + } let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); + // Build the frame layout from the GBM buffer's pitches. + let plane_sizes = gbm_frame.get_plane_size(); let frame_layout = FrameLayout { - format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR + format: (nv12_fourcc(), 0), size: CrosResolution { width: self.coded_width, height: self.coded_height }, planes: vec![ - PlaneLayout { buffer_index: 0, offset: offsets[0], stride: pitches[0] }, - PlaneLayout { buffer_index: 0, offset: offsets[1], stride: pitches[1] }, + PlaneLayout { buffer_index: 0, offset: 0, stride: pitches[0] }, + PlaneLayout { buffer_index: 0, offset: plane_sizes[0], stride: pitches[1] }, ], }; @@ -1090,7 +1013,7 @@ impl StandardVideoEncoder for VaapiAv1Encoder { CrosFrameMetadata { timestamp, layout: frame_layout, force_keyframe: is_keyframe }; self.encoder - .encode(cros_meta, surface) + .encode(cros_meta, gbm_frame) .map_err(|e| format!("VA-API AV1 encode error: {e}"))?; self.frame_count += 1; diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs index fabc59b2..c66d8bbb 100644 --- a/crates/nodes/src/video/vaapi_h264.rs +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -51,7 +51,6 @@ use tokio::sync::mpsc; // cros-codecs high-level APIs. use cros_codecs::backend::vaapi::decoder::VaapiBackend as VaapiDecBackend; -use cros_codecs::backend::vaapi::encoder::VaapiBackend as VaapiEncBackend; use cros_codecs::codec::h264::parser::Level as H264Level; use cros_codecs::codec::h264::parser::Profile as H264Profile; use cros_codecs::decoder::stateless::h264::H264; @@ -63,7 +62,9 @@ use cros_codecs::encoder::{ FrameMetadata as CrosFrameMetadata, PredictionStructure, RateControl, Tunings, VideoEncoder, }; use cros_codecs::libva; -use cros_codecs::video_frame::gbm_video_frame::{GbmUsage, GbmVideoFrame}; +use cros_codecs::video_frame::gbm_video_frame::{ + GbmDevice, GbmExternalBufferDescriptor, GbmUsage, GbmVideoFrame, +}; use cros_codecs::video_frame::VideoFrame as CrosVideoFrame; use cros_codecs::{FrameLayout, PlaneLayout, Resolution as CrosResolution}; @@ -74,8 +75,7 @@ use super::H264_CONTENT_TYPE; // Re-use helpers from the VA-API AV1 module — they are codec-agnostic NV12 // I/O routines (VA surface upload, GBM mapping, render-device detection, etc.). use super::vaapi_av1::{ - align_up_u32, nv12_fourcc, open_va_and_gbm, open_va_display, read_nv12_from_mapping, - write_nv12_to_va_surface, + align_up_u32, nv12_fourcc, open_va_and_gbm, read_nv12_from_mapping, write_nv12_to_mapping, }; // --------------------------------------------------------------------------- @@ -589,17 +589,20 @@ impl EncoderNodeRunner for VaapiH264EncoderNode { // Encoder — internal codec wrapper // --------------------------------------------------------------------------- -/// Type alias for the VA-API H.264 encoder using direct VA surfaces. +/// Type alias for the VA-API H.264 encoder using GBM-backed video frames. /// -/// Bypasses GBM buffer allocation entirely — input frames are uploaded to -/// VA surfaces via the VA-API Image API and passed straight through to the -/// encoder backend. This avoids the `GBM_BO_USE_HW_VIDEO_ENCODER` flag -/// which Mesa's iris driver does not support for NV12 on some hardware -/// (e.g. Intel Tiger Lake with Mesa 23.x). +/// The `GbmVideoFrame` handle satisfies the `VideoFrame` trait bound +/// required by `StatelessEncoder::new_vaapi()`. At runtime, GBM buffer +/// allocation uses `GBM_BO_USE_HW_VIDEO_ENCODER` when supported, and +/// falls back to `GBM_BO_USE_HW_VIDEO_DECODER` on drivers where the +/// encoder flag is unsupported (e.g. Mesa iris on Intel Tiger Lake). type CrosVaapiH264Encoder = StatelessEncoder< cros_codecs::encoder::h264::H264, - libva::Surface<()>, - cros_codecs::backend::vaapi::encoder::VaapiBackend<(), libva::Surface<()>>, + GbmVideoFrame, + cros_codecs::backend::vaapi::encoder::VaapiBackend< + GbmExternalBufferDescriptor, + libva::Surface, + >, >; /// Internal encoder state wrapping the cros-codecs `StatelessEncoder`. @@ -609,6 +612,12 @@ type CrosVaapiH264Encoder = StatelessEncoder< struct VaapiH264Encoder { encoder: CrosVaapiH264Encoder, display: Rc, + gbm: Arc, + /// GBM buffer usage flag. Defaults to `Encode` (optimal tiling for the + /// encoder HW), but falls back to `Decode` on drivers where + /// `GBM_BO_USE_HW_VIDEO_ENCODER` is unsupported (e.g. Mesa iris on + /// Intel Tiger Lake with Mesa 23.x). + gbm_usage: GbmUsage, width: u32, height: u32, coded_width: u32, @@ -621,12 +630,35 @@ impl StandardVideoEncoder for VaapiH264Encoder { const CODEC_NAME: &'static str = "VA-API H.264"; fn new_encoder(width: u32, height: u32, config: &Self::Config) -> Result { - let (display, path) = open_va_display(config.render_device.as_ref())?; + let (display, gbm, path) = open_va_and_gbm(config.render_device.as_ref())?; tracing::info!(device = %path, width, height, "VA-API H.264 encoder opening"); let coded_width = align_up_u32(width, H264_MB_SIZE); let coded_height = align_up_u32(height, H264_MB_SIZE); + // Probe GBM encoder buffer support (see vaapi_av1.rs for details). + let gbm_usage = { + let probe_res = CrosResolution { width: coded_width, height: coded_height }; + match Arc::clone(&gbm).new_frame( + nv12_fourcc(), + probe_res.clone(), + probe_res, + GbmUsage::Encode, + ) { + Ok(_) => { + tracing::debug!("GBM encoder buffer allocation OK"); + GbmUsage::Encode + }, + Err(_) => { + tracing::warn!( + "GBM_BO_USE_HW_VIDEO_ENCODER unsupported on this driver; \ + falling back to GBM_BO_USE_HW_VIDEO_DECODER for encoder input buffers" + ); + GbmUsage::Decode + }, + } + }; + // Auto-detect the correct entrypoint. Modern Intel GPUs (Gen 9+ / // Skylake onwards) only expose the low-power fixed-function encoder // (`VAEntrypointEncSliceLP`), while older hardware and some AMD @@ -680,28 +712,15 @@ impl StandardVideoEncoder for VaapiH264Encoder { }, }; - // Construct the VA-API encoder backend directly instead of using - // `new_vaapi()`, which requires `V: VideoFrame`. Our type alias - // uses `Surface<()>` (bypassing GBM allocation), so we replicate - // the profile mapping and backend construction inline. - let va_profile = libva::VAProfile::VAProfileH264Main; - let bitrate_control = match cros_config.initial_tunings.rate_control { - RateControl::ConstantBitrate(_) => libva::VA_RC_CBR, - RateControl::ConstantQuality(_) => libva::VA_RC_CQP, - }; - let coded_size = CrosResolution { width: coded_width, height: coded_height }; - let backend = VaapiEncBackend::new( + let encoder = CrosVaapiH264Encoder::new_vaapi( Rc::clone(&display), - va_profile, + cros_config, nv12_fourcc(), - coded_size, - bitrate_control, + CrosResolution { width: coded_width, height: coded_height }, low_power, + BlockingMode::Blocking, ) - .map_err(|e| format!("failed to create VA-API H.264 encoder backend: {e}"))?; - - let encoder = CrosVaapiH264Encoder::new_h264(backend, cros_config, BlockingMode::Blocking) - .map_err(|e| format!("failed to create VA-API H.264 encoder: {e}"))?; + .map_err(|e| format!("failed to create VA-API H.264 encoder: {e}"))?; tracing::info!( device = %path, @@ -710,10 +729,21 @@ impl StandardVideoEncoder for VaapiH264Encoder { coded_width, coded_height, quality = config.quality, + gbm_usage = ?gbm_usage, "VA-API H.264 encoder created" ); - Ok(Self { encoder, display, width, height, coded_width, coded_height, frame_count: 0 }) + Ok(Self { + encoder, + display, + gbm, + gbm_usage, + width, + height, + coded_width, + coded_height, + frame_count: 0, + }) } fn encode( @@ -727,36 +757,33 @@ impl StandardVideoEncoder for VaapiH264Encoder { .into()); } - // Create a VA surface and upload NV12 data via the Image API. - // This bypasses GBM buffer allocation (GBM_BO_USE_HW_VIDEO_ENCODER), - // which Mesa's iris driver does not support for NV12 on all hardware. - let nv12_fourcc_val: u32 = nv12_fourcc().into(); - let mut surfaces = self - .display - .create_surfaces( - libva::VA_RT_FORMAT_YUV420, - Some(nv12_fourcc_val), - self.coded_width, - self.coded_height, - Some(libva::UsageHint::USAGE_HINT_ENCODER), - vec![()], - ) - .map_err(|e| format!("failed to create VA surface for encoding: {e}"))?; - let surface = - surfaces.pop().ok_or_else(|| "create_surfaces returned empty vec".to_string())?; - - // Write frame data into the VA surface. - let (pitches, offsets) = write_nv12_to_va_surface(&self.display, &surface, frame)?; + // Allocate a GBM frame and write NV12 data into it. + let visible_res = CrosResolution { width: self.width, height: self.height }; + let coded_res = CrosResolution { width: self.coded_width, height: self.coded_height }; + let mut gbm_frame = Arc::clone(&self.gbm) + .new_frame(nv12_fourcc(), visible_res, coded_res, self.gbm_usage.clone()) + .map_err(|e| format!("failed to allocate GBM frame for encoding: {e}"))?; + + // Write NV12 (or I420→NV12) data into the GBM buffer. + let pitches = gbm_frame.get_plane_pitch(); + { + let mapping = gbm_frame + .map_mut() + .map_err(|e| format!("failed to map GBM frame for writing: {e}"))?; + write_nv12_to_mapping(mapping.as_ref(), frame, &pitches)?; + } let is_keyframe = metadata.as_ref().and_then(|m| m.keyframe).unwrap_or(false); let timestamp = metadata.as_ref().and_then(|m| m.timestamp_us).unwrap_or(self.frame_count); + // Build the frame layout from the GBM buffer's pitches. + let plane_sizes = gbm_frame.get_plane_size(); let frame_layout = FrameLayout { - format: (nv12_fourcc(), 0), // DRM_FORMAT_MOD_LINEAR + format: (nv12_fourcc(), 0), size: CrosResolution { width: self.coded_width, height: self.coded_height }, planes: vec![ - PlaneLayout { buffer_index: 0, offset: offsets[0], stride: pitches[0] }, - PlaneLayout { buffer_index: 0, offset: offsets[1], stride: pitches[1] }, + PlaneLayout { buffer_index: 0, offset: 0, stride: pitches[0] }, + PlaneLayout { buffer_index: 0, offset: plane_sizes[0], stride: pitches[1] }, ], }; @@ -764,7 +791,7 @@ impl StandardVideoEncoder for VaapiH264Encoder { CrosFrameMetadata { timestamp, layout: frame_layout, force_keyframe: is_keyframe }; self.encoder - .encode(cros_meta, surface) + .encode(cros_meta, gbm_frame) .map_err(|e| format!("VA-API H.264 encode error: {e}"))?; self.frame_count += 1; From 46938e7c4ab0340950c5d24c666a3213fa4b82a4 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 16:35:47 +0000 Subject: [PATCH 43/69] fix(nodes): return error on NV12 bounds-check failure In write_nv12_to_mapping, the row-copy and I420 UV interleave paths silently skipped rows when bounds checks failed instead of surfacing the error. This made it impossible to diagnose corrupted frames from mismatched buffer sizes. Change all silent skip patterns to return descriptive error messages with the exact indices and buffer lengths involved. Closes #291 Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 159 ++++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 9 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 7b47f9e0..676731f9 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -259,9 +259,13 @@ pub(super) fn write_nv12_to_mapping( for row in 0..h { let s = row * w; let d = row * y_stride; - if s + w <= src.len() && d + w <= y_plane.len() { - y_plane[d..d + w].copy_from_slice(&src[s..s + w]); + if s + w > src.len() || d + w > y_plane.len() { + return Err(format!( + "NV12 Y row copy out of bounds: src[{}..{}] (len {}), dest[{}..{}] (len {})", + s, s + w, src.len(), d, d + w, y_plane.len() + )); } + y_plane[d..d + w].copy_from_slice(&src[s..s + w]); } } } @@ -278,9 +282,13 @@ pub(super) fn write_nv12_to_mapping( for row in 0..uv_h { let s = row * chroma_w; let d = row * uv_stride; - if s + chroma_w <= src_uv.len() && d + chroma_w <= uv_plane.len() { - uv_plane[d..d + chroma_w].copy_from_slice(&src_uv[s..s + chroma_w]); + if s + chroma_w > src_uv.len() || d + chroma_w > uv_plane.len() { + return Err(format!( + "NV12 UV row copy out of bounds: src[{}..{}] (len {}), dest[{}..{}] (len {})", + s, s + chroma_w, src_uv.len(), d, d + chroma_w, uv_plane.len() + )); } + uv_plane[d..d + chroma_w].copy_from_slice(&src_uv[s..s + chroma_w]); } } } @@ -303,9 +311,13 @@ pub(super) fn write_nv12_to_mapping( for row in 0..h { let s = row * w; let d = row * y_stride; - if s + w <= src.len() && d + w <= y_plane.len() { - y_plane[d..d + w].copy_from_slice(&src[s..s + w]); + if s + w > src.len() || d + w > y_plane.len() { + return Err(format!( + "I420 Y row copy out of bounds: src[{}..{}] (len {}), dest[{}..{}] (len {})", + s, s + w, src.len(), d, d + w, y_plane.len() + )); } + y_plane[d..d + w].copy_from_slice(&src[s..s + w]); } } } @@ -319,10 +331,17 @@ pub(super) fn write_nv12_to_mapping( let u_idx = y_size + row * uv_w + col; let v_idx = y_size + u_plane_size + row * uv_w + col; let dst_idx = row * uv_stride + col * 2; - if u_idx < src.len() && v_idx < src.len() && dst_idx + 1 < uv_plane.len() { - uv_plane[dst_idx] = src[u_idx]; - uv_plane[dst_idx + 1] = src[v_idx]; + if u_idx >= src.len() || v_idx >= src.len() || dst_idx + 1 >= uv_plane.len() + { + return Err(format!( + "I420 UV interleave out of bounds: u_idx={u_idx}, v_idx={v_idx} \ + (src len {}), dst_idx={dst_idx} (dest len {})", + src.len(), + uv_plane.len() + )); } + uv_plane[dst_idx] = src[u_idx]; + uv_plane[dst_idx + 1] = src[v_idx]; } } } @@ -1913,6 +1932,128 @@ mod tests { ); } + /// Verify that encoding at a non-superblock-aligned resolution (e.g. + /// 1280×720, which aligns to coded 1280×768) and decoding back produces + /// frames with the original display resolution, NOT the coded resolution. + /// + /// This catches the issue described in #292: if `cros-codecs` does not set + /// `render_and_frame_size_different=1` with the original display resolution + /// in the AV1 sequence header, decoded output will include visible black + /// padding bars from the superblock alignment. + #[tokio::test] + async fn test_vaapi_av1_resolution_padding_not_leaked() { + if !vaapi_available() { + eprintln!("SKIP: no VA-API device available"); + return; + } + + use crate::test_utils::{ + assert_state_initializing, assert_state_running, assert_state_stopped, + create_test_context, create_test_video_frame, + }; + use std::borrow::Cow; + use std::collections::HashMap; + + let display_w: u32 = 1280; + let display_h: u32 = 720; + let coded_h = align_up_u32(display_h, AV1_SB_SIZE); // 768 + + assert_ne!( + display_h, coded_h, + "test requires a height that needs superblock alignment padding" + ); + + // --- Encode --- + let (enc_input_tx, enc_input_rx) = mpsc::channel(10); + let mut enc_inputs = HashMap::new(); + enc_inputs.insert("in".to_string(), enc_input_rx); + + let (enc_context, enc_sender, mut enc_state_rx) = create_test_context(enc_inputs, 10); + let encoder = VaapiAv1EncoderNode::new(VaapiAv1EncoderConfig { + render_device: None, + hw_accel: HwAccelMode::Auto, + quality: 200, + framerate: 30, + low_power: false, + }) + .unwrap(); + let enc_handle = tokio::spawn(async move { Box::new(encoder).run(enc_context).await }); + + assert_state_initializing(&mut enc_state_rx).await; + assert_state_running(&mut enc_state_rx).await; + + // Send a single solid-color NV12 frame at 1280×720. + let mut frame = create_test_video_frame(display_w, display_h, PixelFormat::Nv12, 0x40); + frame.metadata = Some(PacketMetadata { + timestamp_us: Some(0), + duration_us: Some(33_333), + sequence: Some(0), + keyframe: Some(true), + }); + enc_input_tx.send(Packet::Video(frame)).await.unwrap(); + drop(enc_input_tx); + + assert_state_stopped(&mut enc_state_rx).await; + enc_handle.await.unwrap().unwrap(); + + let encoded_packets = enc_sender.get_packets_for_pin("out").await; + assert!(!encoded_packets.is_empty(), "VA-API AV1 encoder produced no packets"); + + // --- Decode --- + let (dec_input_tx, dec_input_rx) = mpsc::channel(10); + let mut dec_inputs = HashMap::new(); + dec_inputs.insert("in".to_string(), dec_input_rx); + + let (dec_context, dec_sender, mut dec_state_rx) = create_test_context(dec_inputs, 10); + let decoder = VaapiAv1DecoderNode::new(VaapiAv1DecoderConfig::default()).unwrap(); + let dec_handle = tokio::spawn(async move { Box::new(decoder).run(dec_context).await }); + + assert_state_initializing(&mut dec_state_rx).await; + assert_state_running(&mut dec_state_rx).await; + + for packet in encoded_packets { + if let Packet::Binary { data, metadata, .. } = packet { + dec_input_tx + .send(Packet::Binary { + data, + content_type: Some(Cow::Borrowed(AV1_CONTENT_TYPE)), + metadata, + }) + .await + .unwrap(); + } + } + drop(dec_input_tx); + + assert_state_stopped(&mut dec_state_rx).await; + dec_handle.await.unwrap().unwrap(); + + let decoded_packets = dec_sender.get_packets_for_pin("out").await; + assert!(!decoded_packets.is_empty(), "VA-API AV1 decoder produced no frames"); + + for (i, packet) in decoded_packets.iter().enumerate() { + match packet { + Packet::Video(frame) => { + assert_eq!( + frame.width, display_w, + "decoded frame {i} width should be display width {display_w}, got {}", + frame.width + ); + assert_eq!( + frame.height, display_h, + "decoded frame {i} height should be display height {display_h}, \ + got {} (coded_height={coded_h}). This indicates the AV1 sequence \ + header is missing render_and_frame_size_different=1 with the \ + original display resolution — see issue #292.", + frame.height + ); + assert_eq!(frame.pixel_format, PixelFormat::Nv12); + }, + _ => panic!("Expected Video packet from VA-API AV1 decoder"), + } + } + } + /// Verify ForceCpu mode returns an error (VA-API is HW-only). #[test] fn test_vaapi_force_cpu_returns_error() { From 6f4bc24b18aa0c1aceb3564176870d0fead79c1e Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 16:35:59 +0000 Subject: [PATCH 44/69] test(ci): add VA-API test coverage to GPU runner - Add libva-dev to the test-gpu system dependencies install step. - Add cargo test with --features vaapi to the GPU test matrix, running VA-API AV1 encode/decode tests on the self-hosted runner. - Add resolution-padding verification test (issue #292) that encodes at 1280x720 (coded 1280x768) and asserts decoded frames match the original display resolution, not the coded resolution. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- .github/workflows/skit.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/skit.yml b/.github/workflows/skit.yml index a241eb9e..55b1361f 100644 --- a/.github/workflows/skit.yml +++ b/.github/workflows/skit.yml @@ -122,7 +122,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -y libvpx-dev nasm cmake pkg-config libopus-dev + sudo apt-get install -y libvpx-dev nasm cmake pkg-config libopus-dev libva-dev - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master @@ -151,6 +151,7 @@ jobs: run: | cargo test --locked -p streamkit-nodes --features gpu cargo test --locked -p streamkit-nodes --features nvcodec + cargo test --locked -p streamkit-nodes --features vaapi cargo test --locked -p streamkit-engine --features gpu build: From 44342364fa9fe9a163f200dd7b6b5dc10901b99d Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 16:43:08 +0000 Subject: [PATCH 45/69] fix(ci): add libgbm-dev to GPU runner dependencies The cros-codecs VA-API backend links against libgbm for GBM buffer management. Without libgbm-dev the vaapi feature tests fail to link. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- .github/workflows/skit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/skit.yml b/.github/workflows/skit.yml index 55b1361f..cf902361 100644 --- a/.github/workflows/skit.yml +++ b/.github/workflows/skit.yml @@ -122,7 +122,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -y libvpx-dev nasm cmake pkg-config libopus-dev libva-dev + sudo apt-get install -y libvpx-dev nasm cmake pkg-config libopus-dev libva-dev libgbm-dev - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master From 8ea9e00ac0daf8139199efc8938766bf2506b73a Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 16:52:09 +0000 Subject: [PATCH 46/69] fix(nodes): skip VA-API encode tests on decode-only drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NVIDIA's community nvidia-vaapi-driver only supports VA-API decode, not encode. The existing vaapi_available() check only verifies that a VA-API display can be opened, which succeeds on NVIDIA — but the encoder tests then fail because no encode entrypoints exist. Add vaapi_av1_encode_available() and vaapi_h264_encode_available() helpers that probe whether the driver actually supports encoding by attempting to construct the encoder. Encode tests now skip gracefully on decode-only drivers instead of failing. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- crates/nodes/src/video/vaapi_av1.rs | 29 ++++++++++++++++++++-------- crates/nodes/src/video/vaapi_h264.rs | 15 ++++++++++++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/crates/nodes/src/video/vaapi_av1.rs b/crates/nodes/src/video/vaapi_av1.rs index 676731f9..97e963d5 100644 --- a/crates/nodes/src/video/vaapi_av1.rs +++ b/crates/nodes/src/video/vaapi_av1.rs @@ -1684,12 +1684,25 @@ mod tests { libva::Display::open_drm_display(std::path::Path::new(&path)).is_ok() } + /// Check whether the VA-API driver supports AV1 *encoding*. + /// + /// NVIDIA's community `nvidia-vaapi-driver` only supports decode, so + /// encode tests must be skipped on NVIDIA GPUs to avoid false failures. + fn vaapi_av1_encode_available() -> bool { + if !vaapi_available() { + return false; + } + // Try to create the encoder — this probes for AV1 encode entrypoints + // and will fail on drivers that only support decode (e.g. NVIDIA). + VaapiAv1Encoder::new_encoder(64, 64, &VaapiAv1EncoderConfig::default()).is_ok() + } + /// Encoder + Decoder roundtrip: encode 5 NV12 frames, decode them back, /// verify dimensions and pixel format. #[tokio::test] async fn test_vaapi_av1_encode_decode_roundtrip() { - if !vaapi_available() { - eprintln!("SKIP: no VA-API device available"); + if !vaapi_av1_encode_available() { + eprintln!("SKIP: no VA-API AV1 encode support available"); return; } @@ -1785,8 +1798,8 @@ mod tests { /// Verify decoded frames preserve metadata from input packets. #[tokio::test] async fn test_vaapi_av1_metadata_propagation() { - if !vaapi_available() { - eprintln!("SKIP: no VA-API device available"); + if !vaapi_av1_encode_available() { + eprintln!("SKIP: no VA-API AV1 encode support available"); return; } @@ -1881,8 +1894,8 @@ mod tests { /// (exercises the I420→NV12 conversion path). #[tokio::test] async fn test_vaapi_av1_encode_i420_input() { - if !vaapi_available() { - eprintln!("SKIP: no VA-API device available"); + if !vaapi_av1_encode_available() { + eprintln!("SKIP: no VA-API AV1 encode support available"); return; } @@ -1942,8 +1955,8 @@ mod tests { /// padding bars from the superblock alignment. #[tokio::test] async fn test_vaapi_av1_resolution_padding_not_leaked() { - if !vaapi_available() { - eprintln!("SKIP: no VA-API device available"); + if !vaapi_av1_encode_available() { + eprintln!("SKIP: no VA-API AV1 encode support available"); return; } diff --git a/crates/nodes/src/video/vaapi_h264.rs b/crates/nodes/src/video/vaapi_h264.rs index c66d8bbb..02e00579 100644 --- a/crates/nodes/src/video/vaapi_h264.rs +++ b/crates/nodes/src/video/vaapi_h264.rs @@ -1066,12 +1066,23 @@ mod tests { libva::Display::open_drm_display(std::path::Path::new(&path)).is_ok() } + /// Check whether the VA-API driver supports H.264 *encoding*. + /// + /// NVIDIA's community `nvidia-vaapi-driver` only supports decode, so + /// encode tests must be skipped on NVIDIA GPUs to avoid false failures. + fn vaapi_h264_encode_available() -> bool { + if !vaapi_available() { + return false; + } + VaapiH264Encoder::new_encoder(64, 64, &VaapiH264EncoderConfig::default()).is_ok() + } + /// Encoder + Decoder roundtrip: encode 5 NV12 frames, decode them back, /// verify dimensions and pixel format. #[tokio::test] async fn test_vaapi_h264_encode_decode_roundtrip() { - if !vaapi_available() { - eprintln!("SKIP: no VA-API device available"); + if !vaapi_h264_encode_available() { + eprintln!("SKIP: no VA-API H.264 encode support available"); return; } From 4c49959f07a2bd5722f1f79f69599c5d3ec28406 Mon Sep 17 00:00:00 2001 From: StreamKit Devin Date: Sun, 12 Apr 2026 19:56:57 +0000 Subject: [PATCH 47/69] fix(deps): vendor cros-codecs with GbmUsage::Linear support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendor cros-codecs 0.0.6 and add a GbmUsage::Linear variant to GbmDevice::new_frame(). On drivers where neither GBM_BO_USE_HW_VIDEO_ENCODER nor GBM_BO_USE_HW_VIDEO_DECODER is supported for contiguous NV12 allocation (e.g. Mesa iris on Intel Tiger Lake with Mesa ≤ 23.x), the Linear variant falls back to GBM_BO_USE_LINEAR which is universally supported. A [patch.crates-io] entry in the workspace Cargo.toml redirects the cros-codecs dependency to the vendored copy. This patch should be removed once upstream cros-codecs ships the Linear variant. Signed-off-by: StreamKit Devin Co-Authored-By: Claudio Costa --- Cargo.lock | 2 - Cargo.toml | 9 + vendor/cros-codecs/.cargo-ok | 1 + vendor/cros-codecs/.cargo/config.toml | 6 + vendor/cros-codecs/.cargo_vcs_info.json | 6 + vendor/cros-codecs/.clippy.toml | 2 + vendor/cros-codecs/.gitignore | 2 + vendor/cros-codecs/Android.bp | 156 + vendor/cros-codecs/CONTRIBUTING.md | 33 + vendor/cros-codecs/Cargo.lock | 847 +++ vendor/cros-codecs/Cargo.toml | 132 + vendor/cros-codecs/Cargo.toml.orig | 50 + vendor/cros-codecs/DIR_METADATA | 13 + vendor/cros-codecs/LICENSE | 26 + vendor/cros-codecs/METADATA | 16 + vendor/cros-codecs/MODULE_LICENSE_BSD | 0 vendor/cros-codecs/OWNERS | 10 + vendor/cros-codecs/README.md | 71 + vendor/cros-codecs/cargo_embargo.json | 10 + vendor/cros-codecs/ci/config.yaml | 132 + vendor/cros-codecs/ci/lava_job_generate.py | 38 + vendor/cros-codecs/ci/template.yaml | 72 + .../ci/test-cases/cros-codecs-single.yaml | 7 + .../ci/test-cases/cros-codecs.yaml | 7 + vendor/cros-codecs/ci/test-cases/run_tests.py | 112 + vendor/cros-codecs/examples/ccdec/main.rs | 244 + vendor/cros-codecs/examples/ccdec/md5.rs | 195 + vendor/cros-codecs/examples/ccdec/util.rs | 125 + vendor/cros-codecs/examples/ccenc/main.rs | 314 ++ vendor/cros-codecs/examples/ccenc/util.rs | 87 + vendor/cros-codecs/examples/perf_test.rs | 135 + vendor/cros-codecs/rustfmt.toml | 3 + vendor/cros-codecs/simple_test.py | 46 + vendor/cros-codecs/src/backend.rs | 16 + vendor/cros-codecs/src/backend/dummy.rs | 8 + .../cros-codecs/src/backend/dummy/decoder.rs | 149 + vendor/cros-codecs/src/backend/v4l2.rs | 20 + .../cros-codecs/src/backend/v4l2/decoder.rs | 21 + .../src/backend/v4l2/decoder/stateless.rs | 154 + .../cros-codecs/src/backend/v4l2/encoder.rs | 1368 +++++ vendor/cros-codecs/src/backend/vaapi.rs | 270 + .../cros-codecs/src/backend/vaapi/decoder.rs | 331 ++ .../cros-codecs/src/backend/vaapi/encoder.rs | 651 +++ .../src/backend/vaapi/surface_pool.rs | 225 + vendor/cros-codecs/src/bitstream_utils.rs | 768 +++ vendor/cros-codecs/src/c2_wrapper.rs | 381 ++ .../cros-codecs/src/c2_wrapper/c2_decoder.rs | 338 ++ .../cros-codecs/src/c2_wrapper/c2_encoder.rs | 272 + .../src/c2_wrapper/c2_v4l2_decoder.rs | 60 + .../src/c2_wrapper/c2_v4l2_encoder.rs | 131 + .../src/c2_wrapper/c2_vaapi_decoder.rs | 86 + .../src/c2_wrapper/c2_vaapi_encoder.rs | 112 + vendor/cros-codecs/src/codec.rs | 18 + vendor/cros-codecs/src/codec/av1.rs | 9 + vendor/cros-codecs/src/codec/av1/helpers.rs | 178 + vendor/cros-codecs/src/codec/av1/parser.rs | 4216 +++++++++++++++ vendor/cros-codecs/src/codec/av1/reader.rs | 249 + .../cros-codecs/src/codec/av1/synthesizer.rs | 1796 +++++++ .../codec/av1/test_data/av1-annexb.ivf.av1 | Bin 0 -> 579 bytes .../av1/test_data/av1-annexb.ivf.av1.crc | 0 .../src/codec/av1/test_data/gen_crcs.sh | 8 + .../codec/av1/test_data/test-25fps.av1.ivf | Bin 0 -> 223686 bytes .../av1/test_data/test-25fps.av1.ivf.json | 260 + .../codec/av1/test_data/test-25fps.ivf.av1 | Bin 0 -> 223686 bytes .../av1/test_data/test-25fps.ivf.av1.crc | 250 + .../av1/test_data/test-25fps.ivf.av1.md5 | 250 + vendor/cros-codecs/src/codec/av1/writer.rs | 199 + vendor/cros-codecs/src/codec/h264.rs | 10 + vendor/cros-codecs/src/codec/h264/dpb.rs | 1243 +++++ vendor/cros-codecs/src/codec/h264/nalu.rs | 118 + .../cros-codecs/src/codec/h264/nalu_writer.rs | 299 ++ vendor/cros-codecs/src/codec/h264/parser.rs | 3023 +++++++++++ vendor/cros-codecs/src/codec/h264/picture.rs | 415 ++ .../cros-codecs/src/codec/h264/synthesizer.rs | 582 ++ .../h264/test_data/64x64-I-P-B-P-high.h264 | Bin 0 -> 2555 bytes .../test_data/64x64-I-P-B-P-high.h264.crc | 3 + .../test_data/64x64-I-P-B-P-high.h264.md5 | 3 + .../codec/h264/test_data/64x64-I-P-B-P.h264 | Bin 0 -> 2930 bytes .../h264/test_data/64x64-I-P-B-P.h264.crc | 3 + .../h264/test_data/64x64-I-P-B-P.h264.md5 | 3 + .../src/codec/h264/test_data/64x64-I-P.h264 | Bin 0 -> 2542 bytes .../codec/h264/test_data/64x64-I-P.h264.crc | 2 + .../codec/h264/test_data/64x64-I-P.h264.md5 | 2 + .../src/codec/h264/test_data/64x64-I.h264 | Bin 0 -> 2128 bytes .../src/codec/h264/test_data/64x64-I.h264.crc | 1 + .../src/codec/h264/test_data/64x64-I.h264.md5 | 1 + .../src/codec/h264/test_data/README.md | 70 + .../src/codec/h264/test_data/gen_crcs.sh | 8 + .../test-25fps-h264-slice-data-0.bin | Bin 0 -> 2235 bytes .../test-25fps-h264-slice-data-2.bin | Bin 0 -> 2429 bytes .../test-25fps-h264-slice-data-4.bin | 1 + .../h264/test_data/test-25fps-interlaced.h264 | Bin 0 -> 124935 bytes .../test_data/test-25fps-interlaced.h264.crc | 250 + .../test_data/test-25fps-interlaced.h264.md5 | 250 + .../src/codec/h264/test_data/test-25fps.h264 | Bin 0 -> 150148 bytes .../codec/h264/test_data/test-25fps.h264.crc | 250 + .../codec/h264/test_data/test-25fps.h264.json | 260 + .../codec/h264/test_data/test-25fps.h264.md5 | 250 + vendor/cros-codecs/src/codec/h265.rs | 7 + vendor/cros-codecs/src/codec/h265/dpb.rs | 276 + vendor/cros-codecs/src/codec/h265/parser.rs | 4784 +++++++++++++++++ vendor/cros-codecs/src/codec/h265/picture.rs | 154 + .../codec/h265/test_data/64x64-I-P-B-P.h265 | Bin 0 -> 4150 bytes .../h265/test_data/64x64-I-P-B-P.h265.crc | 3 + .../h265/test_data/64x64-I-P-B-P.h265.md5 | 3 + .../src/codec/h265/test_data/64x64-I-P.h265 | Bin 0 -> 3808 bytes .../codec/h265/test_data/64x64-I-P.h265.crc | 2 + .../codec/h265/test_data/64x64-I-P.h265.md5 | 2 + .../src/codec/h265/test_data/64x64-I.h265 | Bin 0 -> 3443 bytes .../src/codec/h265/test_data/64x64-I.h265.crc | 1 + .../src/codec/h265/test_data/64x64-I.h265.md5 | 1 + .../src/codec/h265/test_data/README.md | 17 + .../src/codec/h265/test_data/bbb.h265 | Bin 0 -> 20503 bytes .../src/codec/h265/test_data/bbb.h265.crc | 60 + .../src/codec/h265/test_data/bbb.h265.md5 | 60 + .../src/codec/h265/test_data/bear.h265 | Bin 0 -> 14428 bytes .../src/codec/h265/test_data/bear.h265.crc | 30 + .../src/codec/h265/test_data/bear.h265.md5 | 30 + .../src/codec/h265/test_data/gen_crcs.sh | 8 + .../test-25fps-h265-slice-data-0.bin | Bin 0 -> 8291 bytes .../test-25fps-h265-slice-data-1.bin | Bin 0 -> 2983 bytes .../src/codec/h265/test_data/test-25fps.h265 | Bin 0 -> 114405 bytes .../codec/h265/test_data/test-25fps.h265.crc | 250 + .../codec/h265/test_data/test-25fps.h265.json | 261 + .../codec/h265/test_data/test-25fps.h265.md5 | 250 + vendor/cros-codecs/src/codec/vp8.rs | 11 + .../cros-codecs/src/codec/vp8/bool_decoder.rs | 309 ++ vendor/cros-codecs/src/codec/vp8/parser.rs | 811 +++ vendor/cros-codecs/src/codec/vp8/probs.rs | 365 ++ .../src/codec/vp8/test_data/README.md | 10 + .../src/codec/vp8/test_data/gen_crcs.sh | 8 + .../test-25fps-vp8-probability-table-0.bin | 2 + .../test-25fps-vp8-probability-table-1.bin | 2 + .../test-25fps-vp8-probability-table-2.bin | 2 + .../src/codec/vp8/test_data/test-25fps.vp8 | Bin 0 -> 255719 bytes .../codec/vp8/test_data/test-25fps.vp8.crc | 250 + .../codec/vp8/test_data/test-25fps.vp8.json | 260 + .../codec/vp8/test_data/test-25fps.vp8.md5 | 250 + .../vp8/test_data/vp8-parser-test-0-inter.bin | Bin 0 -> 554 bytes .../vp8/test_data/vp8-parser-test-0-intra.bin | Bin 0 -> 664 bytes vendor/cros-codecs/src/codec/vp9.rs | 10 + vendor/cros-codecs/src/codec/vp9/lookups.rs | 116 + vendor/cros-codecs/src/codec/vp9/parser.rs | 1478 +++++ .../src/codec/vp9/test_data/README.md | 41 + .../src/codec/vp9/test_data/gen_crcs.sh | 8 + .../resolution_change_500frames-vp9.ivf | Bin 0 -> 790703 bytes .../resolution_change_500frames-vp9.ivf.crc | 500 ++ .../resolution_change_500frames-vp9.ivf.md5 | 2 + .../src/codec/vp9/test_data/test-25fps.vp9 | Bin 0 -> 88090 bytes .../codec/vp9/test_data/test-25fps.vp9.crc | 250 + .../codec/vp9/test_data/test-25fps.vp9.json | 260 + .../codec/vp9/test_data/test-25fps.vp9.md5 | 250 + .../codec/vp9/test_data/vp9-superframe.bin | Bin 0 -> 1553 bytes .../vp90-2-10-show-existing-frame.vp9.ivf | Bin 0 -> 174083 bytes .../vp90-2-10-show-existing-frame.vp9.ivf.crc | 13 + .../vp90-2-10-show-existing-frame.vp9.ivf.md5 | 13 + .../vp90-2-10-show-existing-frame2.vp9.ivf | Bin 0 -> 9946 bytes ...vp90-2-10-show-existing-frame2.vp9.ivf.crc | 16 + ...vp90-2-10-show-existing-frame2.vp9.ivf.md5 | 16 + vendor/cros-codecs/src/decoder.rs | 266 + vendor/cros-codecs/src/decoder/stateless.rs | 495 ++ .../cros-codecs/src/decoder/stateless/av1.rs | 565 ++ .../src/decoder/stateless/av1/dummy.rs | 69 + .../src/decoder/stateless/av1/v4l2.rs | 169 + .../src/decoder/stateless/av1/vaapi.rs | 625 +++ .../cros-codecs/src/decoder/stateless/h264.rs | 1501 ++++++ .../src/decoder/stateless/h264/dummy.rs | 75 + .../src/decoder/stateless/h264/v4l2.rs | 180 + .../src/decoder/stateless/h264/vaapi.rs | 733 +++ .../cros-codecs/src/decoder/stateless/h265.rs | 1365 +++++ .../src/decoder/stateless/h265/dummy.rs | 74 + .../src/decoder/stateless/h265/vaapi.rs | 980 ++++ .../cros-codecs/src/decoder/stateless/vp8.rs | 364 ++ .../src/decoder/stateless/vp8/dummy.rs | 53 + .../src/decoder/stateless/vp8/v4l2.rs | 151 + .../src/decoder/stateless/vp8/vaapi.rs | 707 +++ .../cros-codecs/src/decoder/stateless/vp9.rs | 461 ++ .../src/decoder/stateless/vp9/dummy.rs | 51 + .../src/decoder/stateless/vp9/v4l2.rs | 154 + .../src/decoder/stateless/vp9/vaapi.rs | 651 +++ vendor/cros-codecs/src/device.rs | 6 + vendor/cros-codecs/src/device/v4l2.rs | 6 + .../cros-codecs/src/device/v4l2/stateless.rs | 8 + .../src/device/v4l2/stateless/controls.rs | 8 + .../src/device/v4l2/stateless/controls/av1.rs | 689 +++ .../device/v4l2/stateless/controls/h264.rs | 294 + .../src/device/v4l2/stateless/controls/vp8.rs | 183 + .../src/device/v4l2/stateless/controls/vp9.rs | 212 + .../src/device/v4l2/stateless/device.rs | 227 + .../src/device/v4l2/stateless/queue.rs | 447 ++ .../src/device/v4l2/stateless/request.rs | 249 + vendor/cros-codecs/src/device/v4l2/utils.rs | 130 + vendor/cros-codecs/src/encoder.rs | 418 ++ vendor/cros-codecs/src/encoder/av1.rs | 34 + vendor/cros-codecs/src/encoder/h264.rs | 34 + vendor/cros-codecs/src/encoder/h265.rs | 30 + vendor/cros-codecs/src/encoder/stateful.rs | 257 + .../cros-codecs/src/encoder/stateful/h264.rs | 6 + .../src/encoder/stateful/h264/v4l2.rs | 313 ++ .../cros-codecs/src/encoder/stateful/h265.rs | 6 + .../src/encoder/stateful/h265/v4l2.rs | 305 ++ .../cros-codecs/src/encoder/stateful/vp8.rs | 6 + .../src/encoder/stateful/vp8/v4l2.rs | 332 ++ .../cros-codecs/src/encoder/stateful/vp9.rs | 6 + .../src/encoder/stateful/vp9/v4l2.rs | 337 ++ vendor/cros-codecs/src/encoder/stateless.rs | 398 ++ .../cros-codecs/src/encoder/stateless/av1.rs | 133 + .../src/encoder/stateless/av1/predictor.rs | 326 ++ .../src/encoder/stateless/av1/vaapi.rs | 1009 ++++ .../cros-codecs/src/encoder/stateless/h264.rs | 197 + .../src/encoder/stateless/h264/predictor.rs | 269 + .../src/encoder/stateless/h264/vaapi.rs | 695 +++ .../src/encoder/stateless/predictor.rs | 346 ++ .../cros-codecs/src/encoder/stateless/vp9.rs | 122 + .../src/encoder/stateless/vp9/predictor.rs | 138 + .../src/encoder/stateless/vp9/vaapi.rs | 602 +++ vendor/cros-codecs/src/encoder/vp8.rs | 24 + vendor/cros-codecs/src/encoder/vp9.rs | 31 + vendor/cros-codecs/src/image_processing.rs | 551 ++ vendor/cros-codecs/src/lib.rs | 459 ++ .../puppets-480x270_20230825.i420.yuv | 1 + .../puppets-480x270_20230825.mm21.yuv | 1 + .../puppets-480x270_20230825.nv12.yuv | 1 + vendor/cros-codecs/src/utils.rs | 117 + vendor/cros-codecs/src/video_frame.rs | 311 ++ .../cros-codecs/src/video_frame/frame_pool.rs | 115 + .../src/video_frame/gbm_video_frame.rs | 790 +++ .../video_frame/generic_dma_video_frame.rs | 548 ++ .../src/video_frame/v4l2_mmap_video_frame.rs | 158 + vendor/cros-codecs/test/AndroidTest.xml | 28 + vendor/cros-codecs/test/ccdec_test.rs | 124 + 231 files changed, 55926 insertions(+), 2 deletions(-) create mode 100644 vendor/cros-codecs/.cargo-ok create mode 100644 vendor/cros-codecs/.cargo/config.toml create mode 100644 vendor/cros-codecs/.cargo_vcs_info.json create mode 100644 vendor/cros-codecs/.clippy.toml create mode 100644 vendor/cros-codecs/.gitignore create mode 100644 vendor/cros-codecs/Android.bp create mode 100644 vendor/cros-codecs/CONTRIBUTING.md create mode 100644 vendor/cros-codecs/Cargo.lock create mode 100644 vendor/cros-codecs/Cargo.toml create mode 100644 vendor/cros-codecs/Cargo.toml.orig create mode 100644 vendor/cros-codecs/DIR_METADATA create mode 100644 vendor/cros-codecs/LICENSE create mode 100644 vendor/cros-codecs/METADATA create mode 100644 vendor/cros-codecs/MODULE_LICENSE_BSD create mode 100644 vendor/cros-codecs/OWNERS create mode 100644 vendor/cros-codecs/README.md create mode 100644 vendor/cros-codecs/cargo_embargo.json create mode 100644 vendor/cros-codecs/ci/config.yaml create mode 100755 vendor/cros-codecs/ci/lava_job_generate.py create mode 100644 vendor/cros-codecs/ci/template.yaml create mode 100644 vendor/cros-codecs/ci/test-cases/cros-codecs-single.yaml create mode 100644 vendor/cros-codecs/ci/test-cases/cros-codecs.yaml create mode 100644 vendor/cros-codecs/ci/test-cases/run_tests.py create mode 100644 vendor/cros-codecs/examples/ccdec/main.rs create mode 100644 vendor/cros-codecs/examples/ccdec/md5.rs create mode 100644 vendor/cros-codecs/examples/ccdec/util.rs create mode 100644 vendor/cros-codecs/examples/ccenc/main.rs create mode 100644 vendor/cros-codecs/examples/ccenc/util.rs create mode 100644 vendor/cros-codecs/examples/perf_test.rs create mode 100644 vendor/cros-codecs/rustfmt.toml create mode 100644 vendor/cros-codecs/simple_test.py create mode 100644 vendor/cros-codecs/src/backend.rs create mode 100644 vendor/cros-codecs/src/backend/dummy.rs create mode 100644 vendor/cros-codecs/src/backend/dummy/decoder.rs create mode 100644 vendor/cros-codecs/src/backend/v4l2.rs create mode 100644 vendor/cros-codecs/src/backend/v4l2/decoder.rs create mode 100644 vendor/cros-codecs/src/backend/v4l2/decoder/stateless.rs create mode 100644 vendor/cros-codecs/src/backend/v4l2/encoder.rs create mode 100644 vendor/cros-codecs/src/backend/vaapi.rs create mode 100644 vendor/cros-codecs/src/backend/vaapi/decoder.rs create mode 100644 vendor/cros-codecs/src/backend/vaapi/encoder.rs create mode 100644 vendor/cros-codecs/src/backend/vaapi/surface_pool.rs create mode 100644 vendor/cros-codecs/src/bitstream_utils.rs create mode 100644 vendor/cros-codecs/src/c2_wrapper.rs create mode 100644 vendor/cros-codecs/src/c2_wrapper/c2_decoder.rs create mode 100644 vendor/cros-codecs/src/c2_wrapper/c2_encoder.rs create mode 100644 vendor/cros-codecs/src/c2_wrapper/c2_v4l2_decoder.rs create mode 100644 vendor/cros-codecs/src/c2_wrapper/c2_v4l2_encoder.rs create mode 100644 vendor/cros-codecs/src/c2_wrapper/c2_vaapi_decoder.rs create mode 100644 vendor/cros-codecs/src/c2_wrapper/c2_vaapi_encoder.rs create mode 100644 vendor/cros-codecs/src/codec.rs create mode 100644 vendor/cros-codecs/src/codec/av1.rs create mode 100644 vendor/cros-codecs/src/codec/av1/helpers.rs create mode 100644 vendor/cros-codecs/src/codec/av1/parser.rs create mode 100644 vendor/cros-codecs/src/codec/av1/reader.rs create mode 100644 vendor/cros-codecs/src/codec/av1/synthesizer.rs create mode 100644 vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1 create mode 100644 vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1.crc create mode 100755 vendor/cros-codecs/src/codec/av1/test_data/gen_crcs.sh create mode 100644 vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf create mode 100644 vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf.json create mode 100644 vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1 create mode 100644 vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.crc create mode 100644 vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.md5 create mode 100644 vendor/cros-codecs/src/codec/av1/writer.rs create mode 100644 vendor/cros-codecs/src/codec/h264.rs create mode 100644 vendor/cros-codecs/src/codec/h264/dpb.rs create mode 100644 vendor/cros-codecs/src/codec/h264/nalu.rs create mode 100644 vendor/cros-codecs/src/codec/h264/nalu_writer.rs create mode 100644 vendor/cros-codecs/src/codec/h264/parser.rs create mode 100644 vendor/cros-codecs/src/codec/h264/picture.rs create mode 100644 vendor/cros-codecs/src/codec/h264/synthesizer.rs create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.crc create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.md5 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.crc create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.md5 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.crc create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.md5 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.crc create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.md5 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/README.md create mode 100755 vendor/cros-codecs/src/codec/h264/test_data/gen_crcs.sh create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-0.bin create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-2.bin create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-4.bin create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.crc create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.md5 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264 create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.crc create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.json create mode 100644 vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.md5 create mode 100644 vendor/cros-codecs/src/codec/h265.rs create mode 100644 vendor/cros-codecs/src/codec/h265/dpb.rs create mode 100644 vendor/cros-codecs/src/codec/h265/parser.rs create mode 100644 vendor/cros-codecs/src/codec/h265/picture.rs create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.crc create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.md5 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.crc create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.md5 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.crc create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.md5 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/README.md create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/bbb.h265 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.crc create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.md5 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/bear.h265 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/bear.h265.crc create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/bear.h265.md5 create mode 100755 vendor/cros-codecs/src/codec/h265/test_data/gen_crcs.sh create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-0.bin create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-1.bin create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265 create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.crc create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.json create mode 100644 vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.md5 create mode 100644 vendor/cros-codecs/src/codec/vp8.rs create mode 100644 vendor/cros-codecs/src/codec/vp8/bool_decoder.rs create mode 100644 vendor/cros-codecs/src/codec/vp8/parser.rs create mode 100644 vendor/cros-codecs/src/codec/vp8/probs.rs create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/README.md create mode 100755 vendor/cros-codecs/src/codec/vp8/test_data/gen_crcs.sh create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-0.bin create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-1.bin create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-2.bin create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8 create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.crc create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.json create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.md5 create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/vp8-parser-test-0-inter.bin create mode 100644 vendor/cros-codecs/src/codec/vp8/test_data/vp8-parser-test-0-intra.bin create mode 100644 vendor/cros-codecs/src/codec/vp9.rs create mode 100644 vendor/cros-codecs/src/codec/vp9/lookups.rs create mode 100644 vendor/cros-codecs/src/codec/vp9/parser.rs create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/README.md create mode 100755 vendor/cros-codecs/src/codec/vp9/test_data/gen_crcs.sh create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.crc create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.md5 create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9 create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.crc create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.json create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.md5 create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/vp9-superframe.bin create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.crc create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.md5 create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.crc create mode 100644 vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.md5 create mode 100644 vendor/cros-codecs/src/decoder.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/av1.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/av1/dummy.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/av1/v4l2.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/av1/vaapi.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/h264.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/h264/dummy.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/h264/v4l2.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/h264/vaapi.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/h265.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/h265/dummy.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/h265/vaapi.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp8.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp8/dummy.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp8/v4l2.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp8/vaapi.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp9.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp9/dummy.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp9/v4l2.rs create mode 100644 vendor/cros-codecs/src/decoder/stateless/vp9/vaapi.rs create mode 100644 vendor/cros-codecs/src/device.rs create mode 100644 vendor/cros-codecs/src/device/v4l2.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/controls.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/controls/av1.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/controls/h264.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/controls/vp8.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/controls/vp9.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/device.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/queue.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/stateless/request.rs create mode 100644 vendor/cros-codecs/src/device/v4l2/utils.rs create mode 100644 vendor/cros-codecs/src/encoder.rs create mode 100644 vendor/cros-codecs/src/encoder/av1.rs create mode 100644 vendor/cros-codecs/src/encoder/h264.rs create mode 100644 vendor/cros-codecs/src/encoder/h265.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/h264.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/h264/v4l2.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/h265.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/h265/v4l2.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/vp8.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/vp8/v4l2.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/vp9.rs create mode 100644 vendor/cros-codecs/src/encoder/stateful/vp9/v4l2.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/av1.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/av1/predictor.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/av1/vaapi.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/h264.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/h264/predictor.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/h264/vaapi.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/predictor.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/vp9.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/vp9/predictor.rs create mode 100644 vendor/cros-codecs/src/encoder/stateless/vp9/vaapi.rs create mode 100644 vendor/cros-codecs/src/encoder/vp8.rs create mode 100644 vendor/cros-codecs/src/encoder/vp9.rs create mode 100644 vendor/cros-codecs/src/image_processing.rs create mode 100644 vendor/cros-codecs/src/lib.rs create mode 100644 vendor/cros-codecs/src/test_data/puppets-480x270_20230825.i420.yuv create mode 100644 vendor/cros-codecs/src/test_data/puppets-480x270_20230825.mm21.yuv create mode 100644 vendor/cros-codecs/src/test_data/puppets-480x270_20230825.nv12.yuv create mode 100644 vendor/cros-codecs/src/utils.rs create mode 100644 vendor/cros-codecs/src/video_frame.rs create mode 100644 vendor/cros-codecs/src/video_frame/frame_pool.rs create mode 100644 vendor/cros-codecs/src/video_frame/gbm_video_frame.rs create mode 100644 vendor/cros-codecs/src/video_frame/generic_dma_video_frame.rs create mode 100644 vendor/cros-codecs/src/video_frame/v4l2_mmap_video_frame.rs create mode 100644 vendor/cros-codecs/test/AndroidTest.xml create mode 100644 vendor/cros-codecs/test/ccdec_test.rs diff --git a/Cargo.lock b/Cargo.lock index b0e6ba12..a33e59ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1411,8 +1411,6 @@ dependencies = [ [[package]] name = "cros-codecs" version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f7441b4f31c17b6b6b7f57f6c202944aad11d0ab23739a9ff88d8d34dec621" dependencies = [ "anyhow", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index f0964675..9fd80461 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,3 +110,12 @@ cast_sign_loss = "warn" module_name_repetitions = "allow" must_use_candidate = "allow" doc_markdown = "allow" + +# --------------------------------------------------------------------------- +# Patched cros-codecs: adds GbmUsage::Linear for drivers where neither +# GBM_BO_USE_HW_VIDEO_DECODER nor GBM_BO_USE_HW_VIDEO_ENCODER is supported +# (e.g. Mesa iris on Intel Tiger Lake with Mesa 23.x). +# Remove this patch once upstream cros-codecs ships the Linear variant. +# --------------------------------------------------------------------------- +[patch.crates-io] +cros-codecs = { path = "vendor/cros-codecs" } diff --git a/vendor/cros-codecs/.cargo-ok b/vendor/cros-codecs/.cargo-ok new file mode 100644 index 00000000..5f8b7958 --- /dev/null +++ b/vendor/cros-codecs/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/vendor/cros-codecs/.cargo/config.toml b/vendor/cros-codecs/.cargo/config.toml new file mode 100644 index 00000000..7980ab91 --- /dev/null +++ b/vendor/cros-codecs/.cargo/config.toml @@ -0,0 +1,6 @@ +# When cross compiling outside of the chroot +# with --target aarch64-unknown-linux-gnu the +# linker needs to explicity defined for the +# examples to link. +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" diff --git a/vendor/cros-codecs/.cargo_vcs_info.json b/vendor/cros-codecs/.cargo_vcs_info.json new file mode 100644 index 00000000..d997fd7e --- /dev/null +++ b/vendor/cros-codecs/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "7a4211c57ac2e791412f40df2c22fd2cf81104f6" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/vendor/cros-codecs/.clippy.toml b/vendor/cros-codecs/.clippy.toml new file mode 100644 index 00000000..0358cdb5 --- /dev/null +++ b/vendor/cros-codecs/.clippy.toml @@ -0,0 +1,2 @@ +allow-unwrap-in-tests = true +allow-expect-in-tests = true diff --git a/vendor/cros-codecs/.gitignore b/vendor/cros-codecs/.gitignore new file mode 100644 index 00000000..3a64a60e --- /dev/null +++ b/vendor/cros-codecs/.gitignore @@ -0,0 +1,2 @@ +/target +.nlsp-settings diff --git a/vendor/cros-codecs/Android.bp b/vendor/cros-codecs/Android.bp new file mode 100644 index 00000000..14b5b685 --- /dev/null +++ b/vendor/cros-codecs/Android.bp @@ -0,0 +1,156 @@ +package { + default_applicable_licenses: ["system_cros-codecs_license"], + // TODO(b/374841646): This is a temporary assignment. + default_team: "trendy_team_arc_next", +} + +license { + name: "system_cros-codecs_license", + visibility: [":__subpackages__"], + license_kinds: ["SPDX-license-identifier-BSD-3-Clause"], + license_text: ["LICENSE"], +} + +rust_defaults { + name: "libcros_codecs_defaults", + crate_name: "cros_codecs", + cargo_env_compat: true, + cargo_pkg_version: "0.0.5", + crate_root: "src/lib.rs", + edition: "2021", + rustlibs: [ + "libanyhow", + "libcrc32fast", + "libbyteorder", + "libthiserror", + "liblog_rust", + "libgbm_rust", + "libdrm_rust", + "libdrm_fourcc", + "libgbm_sys", + "libnix", + "libzerocopy", + ], + aliases: ["cros_libva:libva"], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + // TODO(b/382119688): Remove this. + lints: "none", + + product_available: true, + vendor_available: true, +} + +rust_library { + name: "libcros_codecs", + defaults: ["libcros_codecs_defaults"], + enabled: false, + arch: { + x86_64: { + enabled: true, + features: [ + "vaapi", + "backend", + ], + rustlibs: ["libcros_libva"], + }, + arm64: { + enabled: true, + features: [ + "v4l2", + "backend", + ], + rustlibs: ["libv4l2r"], + }, + }, +} + +rust_test_host { + name: "libcros_codecs_test", + defaults: ["libcros_codecs_defaults"], + rustlibs: ["libenv_logger"], +} + +rust_test { + name: "ccdec_test", + srcs: ["test/ccdec_test.rs"], + test_suites: ["general-tests"], + test_config: "test/AndroidTest.xml", + rustlibs: [ + "libargh", + "libcros_codecs", + "libenv_logger", + "liblog_rust", + "libserde_json", + ], + data: [ + ":test_data", + ], + data_bins: ["ccdec"], + // TODO(b/394365384): Group shared dependencies with rust_defaults + enabled: false, + arch: { + x86_64: { + enabled: true, + features: [ + "vaapi", + "backend", + ], + }, + arm64: { + enabled: true, + features: [ + "v4l2", + "backend", + ], + }, + }, +} + +rust_test { + name: "ccdec", + srcs: ["examples/ccdec/main.rs"], + test_suites: ["general-tests"], + rustlibs: [ + "libenv_logger", + "libcros_codecs", + "libargh", + "libserde_json", + ], + // TODO(b/394365384): Group shared dependencies with rust_defaults + enabled: false, + arch: { + x86_64: { + enabled: true, + features: [ + "vaapi", + "backend", + ], + }, + arm64: { + enabled: true, + features: [ + "v4l2", + "backend", + ], + }, + }, + test_harness: false, +} + +filegroup { + name: "test_data", + srcs: [ + "src/codec/av1/test_data/*", + "src/codec/h264/test_data/*", + "src/codec/h265/test_data/*", + "src/codec/vp8/test_data/*", + "src/codec/vp9/test_data/*", + ], + exclude_srcs: [ + "src/codec/**/gen_crcs.sh", + "src/codec/**/README.md", + ], +} diff --git a/vendor/cros-codecs/CONTRIBUTING.md b/vendor/cros-codecs/CONTRIBUTING.md new file mode 100644 index 00000000..37ae0a44 --- /dev/null +++ b/vendor/cros-codecs/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. + +## Before you begin + +### Sign our Contributor License Agreement + +Contributions to this project must be accompanied by a +[Contributor License Agreement](https://cla.developers.google.com/about) (CLA). +You (or your employer) retain the copyright to your contribution; this simply +gives us permission to use and redistribute your contributions as part of the +project. + +If you or your current employer have already signed the Google CLA (even if it +was for a different project), you probably don't need to do it again. + +Visit to see your current agreements or to +sign a new one. + +### Review our Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google/conduct/). + +## Contribution process + +### Code Reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. diff --git a/vendor/cros-codecs/Cargo.lock b/vendor/cros-codecs/Cargo.lock new file mode 100644 index 00000000..5537478d --- /dev/null +++ b/vendor/cros-codecs/Cargo.lock @@ -0,0 +1,847 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "argh" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240" +dependencies = [ + "argh_derive", + "argh_shared", + "rust-fuzzy-search", +] + +[[package]] +name = "argh_derive" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803" +dependencies = [ + "argh_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "argh_shared" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6" +dependencies = [ + "serde", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cros-codecs" +version = "0.0.6" +dependencies = [ + "anyhow", + "argh", + "byteorder", + "crc32fast", + "cros-libva", + "drm", + "drm-fourcc", + "env_logger", + "gbm", + "gbm-sys", + "log", + "nix", + "serde_json", + "thiserror", + "v4l2r", + "zerocopy", +] + +[[package]] +name = "cros-libva" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902c9726e953b678595456bd38f95f31aaf1947c56dd9f4a2290f3f1eca4d228" +dependencies = [ + "bindgen 0.70.1", + "bitflags", + "log", + "pkg-config", + "regex", + "thiserror", +] + +[[package]] +name = "drm" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +dependencies = [ + "bitflags", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix", +] + +[[package]] +name = "drm-ffi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +dependencies = [ + "drm-sys", + "rustix", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" +dependencies = [ + "libc", + "linux-raw-sys 0.6.5", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "gbm" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bf55ba6dd53ad0ac115046ff999c5324c283444ee6e0be82454c4e8eb2f36a" +dependencies = [ + "bitflags", + "drm", + "drm-fourcc", + "gbm-sys", + "libc", +] + +[[package]] +name = "gbm-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9cc2f64de9fa707b5c6b2d2f10d7a7e49e845018a9f5685891eb40d3bab2538" +dependencies = [ + "libc", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.2", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "prettyplease" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rust-fuzzy-search" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "v4l2r" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe1d612d2df2a0802020c49a1b029282c45991cdfff1731b5fc61ed3dce4168a" +dependencies = [ + "anyhow", + "bindgen 0.69.5", + "bitflags", + "enumn", + "log", + "nix", + "thiserror", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/vendor/cros-codecs/Cargo.toml b/vendor/cros-codecs/Cargo.toml new file mode 100644 index 00000000..9ee7a954 --- /dev/null +++ b/vendor/cros-codecs/Cargo.toml @@ -0,0 +1,132 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "cros-codecs" +version = "0.0.6" +authors = ["The ChromiumOS Authors"] +description = "Hardware-accelerated codecs for Linux" +readme = "README.md" +license = "BSD-3-Clause" +repository = "https://github.com/chromeos/cros-codecs" + +[[example]] +name = "ccdec" +required-features = ["backend"] + +[[example]] +name = "ccenc" +required-features = ["backend"] + +[[example]] +name = "perf_test" +required-features = ["backend"] + +[dependencies.anyhow] +version = "1.0.75" +optional = true + +[dependencies.byteorder] +version = "1.4.3" +optional = true + +[dependencies.crc32fast] +version = "1.3.2" +optional = true + +[dependencies.drm] +version = "0.12.0" +optional = true + +[dependencies.drm-fourcc] +version = "2.2.0" +optional = true + +[dependencies.gbm] +version = "0.15" +features = ["drm-support"] +optional = true +default-features = false + +[dependencies.gbm-sys] +version = "0.3.1" +optional = true + +[dependencies.libva] +version = "0.0.12" +optional = true +package = "cros-libva" + +[dependencies.log] +version = "0" +features = ["release_max_level_debug"] + +[dependencies.nix] +version = "0.28" +features = [ + "fs", + "event", + "poll", + "mman", + "ioctl", +] +optional = true + +[dependencies.thiserror] +version = "1.0.58" +optional = true + +[dependencies.v4l2r] +version = "0.0.5" +optional = true +package = "v4l2r" + +[dependencies.zerocopy] +version = "0.8.14" +features = ["derive"] +optional = true + +[dev-dependencies.argh] +version = "0.1.12" + +[dev-dependencies.env_logger] +version = "0.9.3" + +[dev-dependencies.serde_json] +version = "1.0.96" + +[features] +backend = [ + "anyhow", + "byteorder", + "thiserror", + "crc32fast", + "nix", + "gbm", + "gbm-sys", + "drm", + "drm-fourcc", + "zerocopy", +] +default = [] +v4l2 = [ + "v4l2r", + "backend", +] +vaapi = [ + "libva", + "backend", +] + +[lints.rust.unexpected_cfgs] +level = "warn" +priority = 0 diff --git a/vendor/cros-codecs/Cargo.toml.orig b/vendor/cros-codecs/Cargo.toml.orig new file mode 100644 index 00000000..06302780 --- /dev/null +++ b/vendor/cros-codecs/Cargo.toml.orig @@ -0,0 +1,50 @@ +[package] +name = "cros-codecs" +version = "0.0.6" +license = "BSD-3-Clause" +description = "Hardware-accelerated codecs for Linux" +repository = "https://github.com/chromeos/cros-codecs" +authors = ["The ChromiumOS Authors"] +edition = "2021" + +[features] +default = [] +backend = ["anyhow", "byteorder", "thiserror", "crc32fast", "nix", "gbm", "gbm-sys", "drm", "drm-fourcc", "zerocopy"] +vaapi = ["libva", "backend"] +v4l2 = ["v4l2r", "backend"] + +[dependencies] +anyhow = { version = "1.0.75", optional = true } +byteorder = { version = "1.4.3", optional = true } +libva = { version = "0.0.12", package = "cros-libva", optional = true } +v4l2r = { version = "0.0.5", package = "v4l2r", optional = true } +log = { version = "0", features = ["release_max_level_debug"] } +thiserror = { version = "1.0.58", optional = true } +crc32fast = { version = "1.3.2", optional = true } +nix = { version = "0.28", features = ["fs", "event", "poll", "mman", "ioctl"], optional = true } +drm = { version = "0.12.0", optional = true } +drm-fourcc = { version = "2.2.0", optional = true } +gbm = { version = "0.15", default-features = false, features = ["drm-support"], optional = true } +gbm-sys = { version = "0.3.1", optional = true } +zerocopy = { version = "0.8.14", optional = true, features = ["derive"] } + +[dev-dependencies] +argh = "0.1.12" +env_logger = "0.9.3" +serde_json = "1.0.96" + +[lints.rust] +# Required to allow #[cfg(fuzzing)] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } + +[[example]] +name = "ccdec" +required-features = ["backend"] + +[[example]] +name = "ccenc" +required-features = ["backend"] + +[[example]] +name = "perf_test" +required-features = ["backend"] diff --git a/vendor/cros-codecs/DIR_METADATA b/vendor/cros-codecs/DIR_METADATA new file mode 100644 index 00000000..f181efa4 --- /dev/null +++ b/vendor/cros-codecs/DIR_METADATA @@ -0,0 +1,13 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +buganizer { + component_id: 168352 # ChromeOS > Platform > Graphics > Video +} + +team_email: "chromeos-gfx-video@google.com" diff --git a/vendor/cros-codecs/LICENSE b/vendor/cros-codecs/LICENSE new file mode 100644 index 00000000..9c1f8394 --- /dev/null +++ b/vendor/cros-codecs/LICENSE @@ -0,0 +1,26 @@ +Copyright 2022 The ChromiumOS Authors + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/cros-codecs/METADATA b/vendor/cros-codecs/METADATA new file mode 100644 index 00000000..10732f0d --- /dev/null +++ b/vendor/cros-codecs/METADATA @@ -0,0 +1,16 @@ +name: "cros-codecs" +description: + "A lightweight, simple, low-dependency, and hopefully safe crate for " + "hardware-accelerated video decoding and encoding on Linux." + +third_party { +homepage: "https://github.com/chromeos/cros-codecs" + identifier { + type: "Git" + value: "https://github.com/chromeos/cros-codecs" + primary_source: false + } + + last_upgrade_date { year: 2024 month: 12 day: 3 } + license_type: NOTICE +} diff --git a/vendor/cros-codecs/MODULE_LICENSE_BSD b/vendor/cros-codecs/MODULE_LICENSE_BSD new file mode 100644 index 00000000..e69de29b diff --git a/vendor/cros-codecs/OWNERS b/vendor/cros-codecs/OWNERS new file mode 100644 index 00000000..d2970454 --- /dev/null +++ b/vendor/cros-codecs/OWNERS @@ -0,0 +1,10 @@ +abodenha@google.com +bselma@google.com +hiroh@google.com +andrescj@google.com +greenjustin@google.com +nhebert@google.com +frkoenig@google.com +bchoobineh@google.com +pmolinalopez@google.com +hnt@google.com diff --git a/vendor/cros-codecs/README.md b/vendor/cros-codecs/README.md new file mode 100644 index 00000000..4da41fc3 --- /dev/null +++ b/vendor/cros-codecs/README.md @@ -0,0 +1,71 @@ +# Cros-codecs + +[crates.io](https://crates.io/crates/cros-codecs) +[docs.rs](https://docs.rs/cros-codecs/latest/cros_codecs/) + +A lightweight, simple, low-dependency, and hopefully safe crate for +hardware-accelerated video decoding and encoding on Linux. + +It is developed for use in ChromeOS (particularly +[crosvm](https://github.com/google/crosvm)), but has no dependency to ChromeOS +and should be usable anywhere. + +## Current features + +- Simple decoder API, +- VAAPI decoder support (using + [cros-libva](https://github.com/chromeos/cros-libva)) for H.264, H.265, VP8, + VP9 and AV1, +- VAAPI encoder support for H.264, VP9 and AV1, +- Stateful V4L2 encoder support. + +## Planned features + +- Stateful V4L2 decoder support, +- Stateless V4L2 decoder support, +- Support for more encoder codecs, +- C API to be used in non-Rust projects. + +## Non-goals + +- Support for systems other than Linux. + +## Example programs + +The `ccdec` example program can decode an encoded stream and write the decoded +frames to a file. As such it can be used for testing purposes. + +```shell +$ cargo build --examples +$ ./target/debug/examples/ccdec --help +Usage: ccdec [--output ] --input-format [--output-format ] [--compute-md5 ] + +Simple player using cros-codecs + +Positional Arguments: + input input file + +Options: + --output output file to write the decoded frames to + --input-format input format to decode from. + --output-format pixel format to decode into. Default: i420 + --compute-md5 whether to display the MD5 of the decoded stream, and at + which granularity (stream or frame) + --help display usage information +``` + +## Testing + +Fluster can be used for testing, using the `ccdec` example program described +above. [This branch](https://github.com/Gnurou/fluster/tree/cros-codecs) +contains support for cros-codecs testing. Just make sure the `ccdec` binary is +in your `PATH`, and run Fluster using one of the `ccdec` decoders, e.g. + +```shell +python fluster.py run -d ccdec-H.264 -ts JVT-AVC_V1 +``` + +## Credits + +The majority of the code in the initial commit has been written by Daniel +Almeida as a VAAPI backend for crosvm, before being split into this crate. diff --git a/vendor/cros-codecs/cargo_embargo.json b/vendor/cros-codecs/cargo_embargo.json new file mode 100644 index 00000000..95c4655f --- /dev/null +++ b/vendor/cros-codecs/cargo_embargo.json @@ -0,0 +1,10 @@ +{ + "package": { + "cros-codecs": { + "host_supported": false + } + }, + "product_available": true, + "vendor_available": true, + "run_cargo": false +} diff --git a/vendor/cros-codecs/ci/config.yaml b/vendor/cros-codecs/ci/config.yaml new file mode 100644 index 00000000..6dda5ed0 --- /dev/null +++ b/vendor/cros-codecs/ci/config.yaml @@ -0,0 +1,132 @@ +intel: + device_type: acer-chromebox-cxi4-puff + codecs: + - vp8: + test-suites: + - vp8-test-vectors: + skip-vectors: [] + - vp9: + test-suites: + - vp9-test-vectors: + skip-vectors: + - vp90-2-22-svc_1280x720_3.ivf + - vp91-2-04-yuv422.webm + - vp91-2-04-yuv444.webm + - h.264: + test-suites: + - JVT-AVC_V1: + skip-vectors: + - CVFC1_Sony_C + - FM1_BT_B + - FM1_FT_E + - FM2_SVA_C + - MR5_TANDBERG_C + - MR8_BT_B + - MR9_BT_B + - SP1_BT_A + - sp2_bt_b + - h.265: + test-suites: + - JCT-VC-HEVC_V1: + skip-vectors: + - CONFWIN_A_Sony_1 + - PICSIZE_A_Bossen_1 + - PICSIZE_B_Bossen_1 + - RAP_B_Bossen_2 + - RPS_C_ericsson_5 + - RPS_E_qualcomm_5 + - TSUNEQBD_A_MAIN10_Technicolor_2 + +amd: + device_type: hp-11A-G6-EE-grunt + codecs: + - h.264: + test-suites: + - JVT-AVC_V1: + skip-vectors: + - CVFC1_Sony_C + - FM1_BT_B + - FM1_FT_E + - FM2_SVA_C + - MR3_TANDBERG_B + - MR4_TANDBERG_C + - MR5_TANDBERG_C + - SP1_BT_A + - sp2_bt_b + - h.265: + test-suites: + - JCT-VC-HEVC_V1: + skip-vectors: + - AMP_D_Hisilicon_3 + - AMP_E_Hisilicon_3 + - CAINIT_A_SHARP_4 + - CAINIT_B_SHARP_4 + - CIP_A_Panasonic_3 + - CIP_C_Panasonic_2 + - CONFWIN_A_Sony_1 + - DBLK_A_MAIN10_VIXS_4 + - DBLK_D_VIXS_2 + - DBLK_E_VIXS_2 + - DBLK_F_VIXS_2 + - DBLK_G_VIXS_2 + - DSLICE_A_HHI_5 + - DSLICE_B_HHI_5 + - DSLICE_C_HHI_5 + - ENTP_B_Qualcomm_1 + - LTRPSPS_A_Qualcomm_1 + - MAXBINS_C_TI_5 + - MERGE_A_TI_3 + - MERGE_B_TI_3 + - MERGE_C_TI_3 + - MERGE_D_TI_3 + - MERGE_E_TI_3 + - MVDL1ZERO_A_docomo_4 + - NoOutPrior_A_Qualcomm_1 + - NoOutPrior_B_Qualcomm_1 + - NUT_A_ericsson_5 + - OPFLAG_A_Qualcomm_1 + - OPFLAG_C_Qualcomm_1 + - PICSIZE_A_Bossen_1 + - PICSIZE_B_Bossen_1 + - PICSIZE_C_Bossen_1 + - PICSIZE_D_Bossen_1 + - PMERGE_A_TI_3 + - PMERGE_B_TI_3 + - PMERGE_C_TI_3 + - PMERGE_D_TI_3 + - PMERGE_E_TI_3 + - RAP_A_docomo_6 + - RAP_B_Bossen_2 + - RPLM_A_qualcomm_4 + - RPLM_B_qualcomm_4 + - RPS_A_docomo_5 + - RPS_C_ericsson_5 + - RPS_E_qualcomm_5 + - RPS_F_docomo_2 + - SAO_A_MediaTek_4 + - SAO_B_MediaTek_5 + - SAO_E_Canon_4 + - SAO_F_Canon_3 + - SAO_G_Canon_3 + - SAODBLK_A_MainConcept_4 + - SAODBLK_B_MainConcept_4 + - SDH_A_Orange_4 + - SLICES_A_Rovi_3 + - SLIST_B_Sony_9 + - SLIST_D_Sony_9 + - TSUNEQBD_A_MAIN10_Technicolor_2 + - WP_A_MAIN10_Toshiba_3 + - WP_B_Toshiba_3 + - WP_MAIN10_B_Toshiba_3 + - WPP_A_ericsson_MAIN10_2 + - WPP_A_ericsson_MAIN_2 + - WPP_B_ericsson_MAIN10_2 + - WPP_B_ericsson_MAIN_2 + - WPP_C_ericsson_MAIN10_2 + - WPP_C_ericsson_MAIN_2 + - WPP_D_ericsson_MAIN10_2 + - WPP_D_ericsson_MAIN_2 + - WPP_E_ericsson_MAIN10_2 + - WPP_E_ericsson_MAIN_2 + - WPP_F_ericsson_MAIN10_2 + - WPP_F_ericsson_MAIN_2 diff --git a/vendor/cros-codecs/ci/lava_job_generate.py b/vendor/cros-codecs/ci/lava_job_generate.py new file mode 100755 index 00000000..f0d41c10 --- /dev/null +++ b/vendor/cros-codecs/ci/lava_job_generate.py @@ -0,0 +1,38 @@ +#!/bin/env python3 + +import argparse +import jinja2 +import os +import yaml + +def get_device_type(arch, config_file): + with open(config_file, "r") as stream: + try: + config = yaml.safe_load(stream) + return config[arch]['device_type'] + except yaml.YAMLError as exc: + print(exc) + +def main(): + argparser = argparse.ArgumentParser() + argparser.add_argument('--template', help='Input template file', required=True) + argparser.add_argument('--test-branch', help='The branch being tested', default='main') + argparser.add_argument('--test-repo', help='The repository being tested', required=True) + argparser.add_argument('--arch', choices=['amd', 'intel'], help='Architecture', required=True) + argparser.add_argument('--ccdec-build-id', help='ccdec build id', required=True) + argparser.add_argument('--token', help='Github read token', required=True) + argparser.add_argument('--repo', help='Github repository', required=True) + argparser.add_argument('--config-file', help='Configuration file', required=True) + args = argparser.parse_args() + + env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(args.template)), + undefined=jinja2.StrictUndefined) + + template = env.get_template(os.path.basename(args.template)) + + print(template.render(ccdec_build_id=args.ccdec_build_id, arch=args.arch, device_type=get_device_type(args.arch, args.config_file), test_branch=args.test_branch, repo_url=args.test_repo, token=args.token, repo=args.repo)) + + +if __name__ == '__main__': + main() + diff --git a/vendor/cros-codecs/ci/template.yaml b/vendor/cros-codecs/ci/template.yaml new file mode 100644 index 00000000..0c4112a4 --- /dev/null +++ b/vendor/cros-codecs/ci/template.yaml @@ -0,0 +1,72 @@ +context: + extra_kernel_args: console_msg_format=syslog earlycon +device_type: {{ device_type }} +job_name: Test cros-codecs on {{ arch }} + +priority: medium +timeouts: + action: + minutes: 120 + actions: + power-off: + seconds: 30 + job: + minutes: 120 + queue: + days: 2 +visibility: public + +actions: +- deploy: + namespace: cros-codecs + kernel: + url: https://storage.chromeos.kernelci.org/images/kernel/cros---chromeos-6.1-x86_64-chromiumos-x86_64.flavour.config+x86-chromebook/clang-14/kernel/bzImage + modules: + compression: xz + url: https://storage.chromeos.kernelci.org/images/kernel/cros---chromeos-6.1-x86_64-chromiumos-x86_64.flavour.config+x86-chromebook/clang-14/modules.tar.xz + nfsrootfs: + compression: xz + url: https://storage.kernelci.org/images/rootfs/debian/bullseye-gst-fluster/20231117.0/amd64/full.rootfs.tar.xz + os: oe + ramdisk: + compression: gz + url: https://storage.kernelci.org/images/rootfs/debian/bullseye-gst-fluster/20231117.0/amd64/initrd.cpio.gz + timeout: + minutes: 15 + to: tftp +- boot: + commands: nfs + method: depthcharge + namespace: cros-codecs + prompts: + - '/ #' + timeout: + minutes: 5 +- test: + definitions: + - repository: {{ repo_url }} + branch: '{{ test_branch }}' + history: False + from: git + name: run_fluster + path: ci/test-cases/cros-codecs.yaml + params: + CCDEC_BUILD_ID: {{ ccdec_build_id }} + ARCH: {{ arch }} + GITHUB_TOKEN: {{ token }} + GITHUB_REPO: {{ repo }} + - repository: {{ repo_url }} + branch: '{{ test_branch }}' + history: False + from: git + name: run_fluster_single + path: ci/test-cases/cros-codecs-single.yaml + params: + CCDEC_BUILD_ID: {{ ccdec_build_id }} + ARCH: {{ arch }} + GITHUB_TOKEN: {{ token }} + GITHUB_REPO: {{ repo }} + namespace: cros-codecs + timeout: + minutes: 120 + diff --git a/vendor/cros-codecs/ci/test-cases/cros-codecs-single.yaml b/vendor/cros-codecs/ci/test-cases/cros-codecs-single.yaml new file mode 100644 index 00000000..e31a191e --- /dev/null +++ b/vendor/cros-codecs/ci/test-cases/cros-codecs-single.yaml @@ -0,0 +1,7 @@ +metadata: + name: cros-codecs-fluster-single + description: "Test cros-codecs in a single thread" + +run: + steps: + - python3 ci/test-cases/run_tests.py --config-file ci/config.yaml --arch ${ARCH} --ccdec-build-id ${CCDEC_BUILD_ID} --token ${GITHUB_TOKEN} --repo ${GITHUB_REPO} --single diff --git a/vendor/cros-codecs/ci/test-cases/cros-codecs.yaml b/vendor/cros-codecs/ci/test-cases/cros-codecs.yaml new file mode 100644 index 00000000..32682e40 --- /dev/null +++ b/vendor/cros-codecs/ci/test-cases/cros-codecs.yaml @@ -0,0 +1,7 @@ +metadata: + name: cros-codecs-fluster + description: "Test cros-codecs" + +run: + steps: + - python3 ci/test-cases/run_tests.py --config-file ci/config.yaml --arch ${ARCH} --ccdec-build-id ${CCDEC_BUILD_ID} --token ${GITHUB_TOKEN} --repo ${GITHUB_REPO} diff --git a/vendor/cros-codecs/ci/test-cases/run_tests.py b/vendor/cros-codecs/ci/test-cases/run_tests.py new file mode 100644 index 00000000..fa1b5ccd --- /dev/null +++ b/vendor/cros-codecs/ci/test-cases/run_tests.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 + +import argparse +import os +import stat +import subprocess +import yaml +import requests +import time + +URL_RUNS="https://api.github.com/repos/{repo}/actions/runs?head-sha={head_sha}&head-branch={head_branch}&per_page=2" +URL_RUN="https://api.github.com/repos/{repo}/actions/runs/{run_id}" + +def run_fluster(codec, test_suite, skips, single_thread): + print(f" {codec} -> {test_suite} (skip: {skips})") + cmd = ['python3', '/usr/bin/fluster_parser.py', '-ts', test_suite, '-d', f"ccdec-{codec}", '-t' '300'] + + if single_thread: + cmd.extend(['-j', '1']) + if skips: + for index, skip in enumerate(skips): + cmd.extend(['-sv', skip] if not index else [skip]) + + print(cmd) + subprocess.run(cmd, check=False) + +def retrieve_ccdec_github(sha, branch, repo, token): + if os.path.exists("/opt/cros-codecs/ccdec"): + os.environ['PATH'] = os.environ['PATH'] + ":/opt/cros-codecs" + return True + + runs = requests.get(URL_RUNS.format(head_sha=sha, head_branch=branch, repo=repo), headers={"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", "Authorization": f"Bearer {token}"}).json() + + found = False + + for run in runs['workflow_runs']: + if run['name'] != 'Health check': + continue + + artifacts = requests.get(run['artifacts_url'], headers={"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", "Authorization": f"Bearer {token}"}).json() + + if artifacts['total_count'] == 0: + break + + for artifact in artifacts['artifacts']: + if artifact['name'] != 'ccdec-bin': + continue + + r = requests.get(artifact['archive_download_url'], headers={"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", "Authorization": f"Bearer {token}"}, stream=True) + + if not os.path.exists("/opt/cros-codecs"): + os.mkdir("/opt/cros-codecs") + + with open("/opt/cros-codecs/ccdec.zip", 'wb') as fd: + for chunk in r.iter_content(chunk_size=128): + fd.write(chunk) + + subprocess.run(['unzip', '/opt/cros-codecs/ccdec.zip', '-d', '/opt/cros-codecs/']) + os.chmod("/opt/cros-codecs/ccdec", mode=(stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)) + os.environ['PATH'] = os.environ['PATH'] + ":/opt/cros-codecs" + + found = True + + break + + break + + return found + +def retrieve_ccdec(run_id, repo, token): + # Retrieve built sha and branch + run = requests.get(URL_RUN.format(run_id=run_id, repo=repo), headers={"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", "Authorization": f"Bearer {token}"}).json() + sha = run['head_sha'] + branch = run['head_branch'] + + # Retrieve the artifact + for i in range(30): + try: + if retrieve_ccdec_github(sha, branch, repo, token): + break + time.sleep(10) + except Exception as e: + print(e) + + +argparser = argparse.ArgumentParser() +argparser.add_argument('--arch', choices=['amd', 'intel'], help='Architecture', required=True) +argparser.add_argument('--config-file', help='Configuration file', required=True) +argparser.add_argument('--ccdec-build-id', help='ccded binary build id', required=True) +argparser.add_argument('--token', help='Github read token', required=True) +argparser.add_argument('--repo', help='Github git repository', required=True) +argparser.add_argument('--single', help='Run in a single thread', action='store_true') +args = argparser.parse_args() + +retrieve_ccdec(args.ccdec_build_id, args.repo, args.token) + +with open(args.config_file, "r") as stream: + try: + config = yaml.safe_load(stream) + for arch, arch_info in config.items(): + if arch != args.arch: + continue + device_type=arch_info['device_type'] + for c in arch_info['codecs']: + for codec, test_suites in c.items(): + for ts in test_suites['test-suites']: + for test_suite in ts: + skips=ts[test_suite]["skip-vectors"] + run_fluster(codec, test_suite, skips, args.single) + break + except yaml.YAMLError as exc: + print(exc) diff --git a/vendor/cros-codecs/examples/ccdec/main.rs b/vendor/cros-codecs/examples/ccdec/main.rs new file mode 100644 index 00000000..9f918d31 --- /dev/null +++ b/vendor/cros-codecs/examples/ccdec/main.rs @@ -0,0 +1,244 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! ccdec, a simple decoder program using cros-codecs. Capable of computing MD5 checksums from the +//! input and writing the raw decoded frames to a file. + +use std::borrow::Cow; +use std::fs::File; +use std::io::Read; +use std::io::Write; +use std::path::PathBuf; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; +use std::time::Duration; + +use std::sync::atomic::{AtomicU64, Ordering}; + +use cros_codecs::bitstream_utils::IvfIterator; +use cros_codecs::bitstream_utils::NalIterator; +use cros_codecs::c2_wrapper::c2_decoder::C2DecoderWorker; +#[cfg(feature = "v4l2")] +use cros_codecs::c2_wrapper::c2_v4l2_decoder::C2V4L2Decoder; +#[cfg(feature = "v4l2")] +use cros_codecs::c2_wrapper::c2_v4l2_decoder::C2V4L2DecoderOptions; +#[cfg(feature = "vaapi")] +use cros_codecs::c2_wrapper::c2_vaapi_decoder::C2VaapiDecoder; +#[cfg(feature = "vaapi")] +use cros_codecs::c2_wrapper::c2_vaapi_decoder::C2VaapiDecoderOptions; +use cros_codecs::c2_wrapper::C2DecodeJob; +use cros_codecs::c2_wrapper::C2Status; +use cros_codecs::c2_wrapper::C2Wrapper; +use cros_codecs::c2_wrapper::DrainMode; +use cros_codecs::codec::h264::parser::Nalu as H264Nalu; +use cros_codecs::codec::h265::parser::Nalu as H265Nalu; +use cros_codecs::decoder::StreamInfo; +use cros_codecs::image_processing::nv12_to_i420; +use cros_codecs::utils::align_up; +use cros_codecs::video_frame::frame_pool::FramePool; +use cros_codecs::video_frame::frame_pool::PooledVideoFrame; +use cros_codecs::video_frame::gbm_video_frame::GbmDevice; +use cros_codecs::video_frame::gbm_video_frame::GbmUsage; +use cros_codecs::video_frame::generic_dma_video_frame::GenericDmaVideoFrame; +use cros_codecs::video_frame::VideoFrame; +use cros_codecs::video_frame::UV_PLANE; +use cros_codecs::video_frame::Y_PLANE; +use cros_codecs::DecodedFormat; +use cros_codecs::EncodedFormat; +use cros_codecs::Fourcc; + +use crate::md5::md5_digest; +use crate::md5::MD5Context; +use crate::util::decide_output_file_name; +use crate::util::golden_md5s; +use crate::util::Args; +use crate::util::Md5Computation; + +mod md5; +mod util; + +// Returns the frame iterator for IVF file. +fn create_vpx_frame_iterator(input: &[u8]) -> Box> + '_> { + Box::new(IvfIterator::new(input).map(Cow::Borrowed)) +} + +fn main() { + env_logger::init(); + + let args: Args = argh::from_env(); + + let mut input = File::open(&args.input).expect("error opening input file"); + + assert!( + args.output_format == DecodedFormat::I420, + "Only I420 currently supported by VA-API ccdec" + ); + + let input = { + let mut buf = Vec::new(); + input.read_to_end(&mut buf).expect("error reading input file"); + buf + }; + + let mut output = if !args.multiple_output_files { + args.output.as_ref().map(|p| File::create(p).expect("error creating output file")) + } else { + None + }; + + let golden_iter = Arc::new(Mutex::new(golden_md5s(&args.golden).into_iter())); + + let frame_iter = + match args.input_format { + EncodedFormat::H264 => Box::new(NalIterator::::new(&input)) + as Box>>, + EncodedFormat::H265 => Box::new(NalIterator::::new(&input)) + as Box>>, + _ => create_vpx_frame_iterator(&input), + }; + + let mut _md5_context = Arc::new(Mutex::new(MD5Context::new())); + let md5_context = _md5_context.clone(); + + let mut output_filename_idx = 0; + let need_per_frame_md5 = match args.compute_md5 { + Some(Md5Computation::Frame) => true, + _ => args.golden.is_some(), + }; + let stream_mode = Some(Md5Computation::Stream) == args.compute_md5; + + let gbm_device = Arc::new( + GbmDevice::open(PathBuf::from("/dev/dri/renderD128")).expect("Could not open GBM device!"), + ); + let framepool = Arc::new(Mutex::new(FramePool::new(move |stream_info: &StreamInfo| { + as Clone>::clone(&gbm_device) + .new_frame( + Fourcc::from(b"NV12"), + stream_info.display_resolution, + stream_info.coded_resolution, + GbmUsage::Decode, + ) + .expect("Could not allocate frame for frame pool!") + .to_generic_dma_video_frame() + .expect("Could not export GBM frame to DMA frame!") + }))); + // This is a workaround to get "copy by clone" semantics for closure variable capture since + // Rust lacks the appropriate syntax to express this concept. + let _framepool = framepool.clone(); + let framepool_hint_cb = move |stream_info: StreamInfo| { + (*_framepool.lock().unwrap()).resize(&stream_info); + }; + let alloc_cb = move || (*framepool.lock().unwrap()).alloc(); + + let frames_needed = Arc::new(AtomicU64::new((*golden_iter.lock().unwrap()).len() as u64)); + + let _frames_needed = frames_needed.clone(); + let on_new_frame = move |job: C2DecodeJob>| { + if args.output.is_none() && args.compute_md5.is_none() && args.golden.is_none() { + return; + } + let width = job.output.as_ref().unwrap().resolution().width as usize; + let height = job.output.as_ref().unwrap().resolution().height as usize; + let luma_size = job.output.as_ref().unwrap().resolution().get_area(); + let chroma_size = align_up(width, 2) / 2 * align_up(height, 2) / 2; + let mut frame_data: Vec = vec![0; luma_size + 2 * chroma_size]; + let (dst_y, dst_uv) = frame_data.split_at_mut(luma_size); + let (dst_u, dst_v) = dst_uv.split_at_mut(chroma_size); + { + let src_pitches = job.output.as_ref().unwrap().get_plane_pitch(); + let src_mapping = + job.output.as_ref().unwrap().map().expect("Failed to map output frame!"); + let src_planes = src_mapping.get(); + nv12_to_i420( + src_planes[Y_PLANE], + src_pitches[Y_PLANE], + dst_y, + width, + src_planes[UV_PLANE], + src_pitches[UV_PLANE], + dst_u, + align_up(width, 2) / 2, + dst_v, + align_up(width, 2) / 2, + width, + height, + ); + } + + if args.multiple_output_files { + let file_name = decide_output_file_name( + args.output.as_ref().expect("multiple_output_files need output to be set"), + output_filename_idx, + ); + + let mut output = File::create(file_name).expect("error creating output file"); + output_filename_idx += 1; + output.write_all(&frame_data).expect("failed to write to output file"); + } else if let Some(output) = &mut output { + output.write_all(&frame_data).expect("failed to write to output file"); + } + + let frame_md5: String = + if need_per_frame_md5 { md5_digest(&frame_data) } else { "".to_string() }; + + match args.compute_md5 { + None => (), + Some(Md5Computation::Frame) => println!("{}", frame_md5), + Some(Md5Computation::Stream) => (*md5_context.lock().unwrap()).consume(&frame_data), + } + + if args.golden.is_some() { + assert_eq!(frame_md5, (*golden_iter.lock().unwrap()).next().unwrap()); + (*_frames_needed).fetch_sub(1, Ordering::SeqCst); + } + }; + + let error_cb = move |_status: C2Status| { + panic!("Unrecoverable decoding error!"); + }; + + #[cfg(feature = "vaapi")] + let mut decoder: C2Wrapper<_, C2DecoderWorker<_, C2VaapiDecoder>> = C2Wrapper::new( + Fourcc::from(args.input_format), + // TODO: Support other pixel formats + Fourcc::from(b"NV12"), + error_cb, + on_new_frame, + framepool_hint_cb, + alloc_cb, + C2VaapiDecoderOptions { libva_device_path: args.libva_device }, + ); + #[cfg(feature = "v4l2")] + let mut decoder: C2Wrapper<_, C2DecoderWorker<_, C2V4L2Decoder>> = C2Wrapper::new( + Fourcc::from(args.input_format), + // TODO: Support other pixel formats + Fourcc::from(b"NV12"), + error_cb, + on_new_frame, + framepool_hint_cb, + alloc_cb, + C2V4L2DecoderOptions { video_device_path: None }, + ); + let _ = decoder.start(); + + for input_frame in frame_iter { + decoder.queue(vec![C2DecodeJob { + input: input_frame.as_ref().to_vec(), + ..Default::default() + }]); + } + decoder.drain(DrainMode::EOSDrain); + + while decoder.is_alive() { + thread::sleep(Duration::from_millis(10)); + } + decoder.stop(); + + assert!((*frames_needed).load(Ordering::SeqCst) == 0, "Not all frames were output."); + + if stream_mode { + println!("{}", (*_md5_context.lock().unwrap()).flush()); + } +} diff --git a/vendor/cros-codecs/examples/ccdec/md5.rs b/vendor/cros-codecs/examples/ccdec/md5.rs new file mode 100644 index 00000000..ebbd6bc4 --- /dev/null +++ b/vendor/cros-codecs/examples/ccdec/md5.rs @@ -0,0 +1,195 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Simple implementation of MD5 based heavily around the pseudocode in the +// Wikipedia article on this topic: https://en.wikipedia.org/wiki/MD5 + +// The Wikipedia article pseudocode is not idiomatic Rust. To keep the variables +// as closely matched to the pseudocode some of the warning are turned off. +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +const shift_amounts: [u8; 64] = [ + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, + 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, + 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, +]; + +const K: [u32; 64] = [ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, +]; + +const init_a: u32 = 0x67452301; +const init_b: u32 = 0xefcdab89; +const init_c: u32 = 0x98badcfe; +const init_d: u32 = 0x10325476; + +fn bytes_to_words(chunk: &[u8]) -> Vec { + assert_eq!(chunk.len() % 4, 0); + let mut ret: Vec = vec![0; chunk.len() / 4]; + for i in 0..chunk.len() { + ret[i / 4] |= u32::from(chunk[i]) << ((i % 4) * 8); + } + + ret +} + +fn left_rotate(x: u32, n: u8) -> u32 { + (x << n) | (x >> (32 - n)) +} + +fn little_to_big_endian(x: u32) -> u32 { + ((x >> 24) & 0x000000FF) + | ((x >> 8) & 0x0000FF00) + | ((x << 8) & 0x00FF0000) + | ((x << 24) & 0xFF000000) +} + +#[allow(unused_comparisons)] +fn process_chunk(byte_chunk: &[u8], a0: u32, b0: u32, c0: u32, d0: u32) -> (u32, u32, u32, u32) { + assert_eq!(byte_chunk.len(), 64); + + let mut A = a0; + let mut B = b0; + let mut C = c0; + let mut D = d0; + + let chunk = bytes_to_words(byte_chunk); + + for i in 0..64 { + let mut F: u32 = 0; + let mut g: usize = 0; + if (0..=15).contains(&i) { + F = (B & C) | ((!B) & D); + g = i + } else if (16..=31).contains(&i) { + F = (D & B) | ((!D) & C); + g = (5 * i + 1) % 16; + } else if (32..=47).contains(&i) { + F = B ^ C ^ D; + g = (3 * i + 5) % 16; + } else if (48..=63).contains(&i) { + F = C ^ (B | (!D)); + g = (7 * i) % 16; + } + F = F.wrapping_add(A).wrapping_add(K[i]).wrapping_add(chunk[g]); + A = D; + D = C; + C = B; + B = B.wrapping_add(left_rotate(F, shift_amounts[i])); + } + + (a0.wrapping_add(A), b0.wrapping_add(B), c0.wrapping_add(C), d0.wrapping_add(D)) +} + +fn pad(input: &[u8], len_already_processed: u64) -> Vec { + let orig_len = (input.len() as u64).wrapping_mul(8).wrapping_add(len_already_processed); + let mut ret = input.to_vec(); + + ret.push(0x80); + while (ret.len() % 64) != 56 { + ret.push(0x00); + } + + ret.push((orig_len & 0xFF) as u8); + ret.push(((orig_len >> 8) & 0xFF) as u8); + ret.push(((orig_len >> 16) & 0xFF) as u8); + ret.push(((orig_len >> 24) & 0xFF) as u8); + ret.push(((orig_len >> 32) & 0xFF) as u8); + ret.push(((orig_len >> 40) & 0xFF) as u8); + ret.push(((orig_len >> 48) & 0xFF) as u8); + ret.push(((orig_len >> 56) & 0xFF) as u8); + + ret +} + +fn md5_digest_padded( + padded_input: &[u8], + mut A: u32, + mut B: u32, + mut C: u32, + mut D: u32, +) -> String { + assert_eq!(padded_input.len() % 64, 0); + + for i in 0..(padded_input.len() / 64) { + (A, B, C, D) = process_chunk(&padded_input[(i * 64)..((i + 1) * 64)], A, B, C, D); + } + + // The final hash should just be bytes, not little endian words. + A = little_to_big_endian(A); + B = little_to_big_endian(B); + C = little_to_big_endian(C); + D = little_to_big_endian(D); + + format!("{:08x}{:08x}{:08x}{:08x}", A, B, C, D) +} + +pub fn md5_digest(input: &[u8]) -> String { + let padded_input = pad(input, 0); + md5_digest_padded(&padded_input, init_a, init_b, init_c, init_d) +} + +pub struct MD5Context { + A: u32, + B: u32, + C: u32, + D: u32, + len_already_processed: u64, + buffer: Vec, +} + +impl MD5Context { + pub fn new() -> Self { + Self { + A: init_a, + B: init_b, + C: init_c, + D: init_d, + len_already_processed: 0, + buffer: Vec::new(), + } + } + + pub fn consume(&mut self, input: &[u8]) { + self.buffer.append(&mut input.to_vec()); + while self.buffer.len() >= 64 { + (self.A, self.B, self.C, self.D) = + process_chunk(&self.buffer[0..64], self.A, self.B, self.C, self.D); + self.len_already_processed = self.len_already_processed.wrapping_add(64 * 8); + self.buffer.drain(0..64); + } + } + + pub fn flush(&mut self) -> String { + let padded_buffer = pad(&self.buffer[..], self.len_already_processed); + let ret = md5_digest_padded(&padded_buffer, self.A, self.B, self.C, self.D); + *self = Self::new(); + ret + } +} + +#[cfg(test)] +mod tests { + use super::md5_digest; + + #[test] + fn digest_simple_strings() { + assert_eq!(md5_digest("".as_bytes()), "d41d8cd98f00b204e9800998ecf8427e"); + assert_eq!( + md5_digest("The quick brown fox jumps over the lazy dog".as_bytes()), + "9e107d9d372bb6826bd81d3542a419d6" + ); + assert_eq!( + md5_digest("The quick brown fox jumps over the lazy dog.".as_bytes()), + "e4d909c290d0fb1ca068ffaddf22cbd0" + ); + } +} diff --git a/vendor/cros-codecs/examples/ccdec/util.rs b/vendor/cros-codecs/examples/ccdec/util.rs new file mode 100644 index 00000000..7b91afd7 --- /dev/null +++ b/vendor/cros-codecs/examples/ccdec/util.rs @@ -0,0 +1,125 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::ffi::OsStr; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::path::PathBuf; +use std::str::FromStr; + +use argh::FromArgs; + +use cros_codecs::DecodedFormat; +use cros_codecs::EncodedFormat; +use cros_codecs::FrameMemoryType; + +#[derive(Debug, Eq, PartialEq)] +pub enum Md5Computation { + Stream, + Frame, +} + +impl FromStr for Md5Computation { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "stream" => Ok(Md5Computation::Stream), + "frame" => Ok(Md5Computation::Frame), + _ => Err("unrecognized MD5 computation option. Valid values: stream, frame"), + } + } +} + +/// Simple player using cros-codecs +#[derive(Debug, FromArgs)] +pub struct Args { + /// input file + #[argh(positional)] + pub input: PathBuf, + + /// output file to write the decoded frames to + #[argh(option)] + pub output: Option, + + /// whether to decode a frame per file. Requires "output" to be set. + #[argh(switch)] + pub multiple_output_files: bool, + + /// input format to decode from. + #[argh(option)] + pub input_format: EncodedFormat, + + /// pixel format to decode into. Default: i420 + #[argh(option, default = "DecodedFormat::I420")] + pub output_format: DecodedFormat, + + /// origin of the memory for decoded buffers (managed, prime or user). Default: managed. + #[allow(dead_code)] + #[argh(option, default = "FrameMemoryType::Managed")] + pub frame_memory: FrameMemoryType, + + /// path to the GBM device to use if frame-memory=prime + #[allow(dead_code)] + #[argh(option)] + pub gbm_device: Option, + + /// path to VA-API device. This option is ignored on V4L2 systems. + #[argh(option)] + #[allow(dead_code)] + pub libva_device: Option, + + /// whether to display the MD5 of the decoded stream, and at which granularity (stream or + /// frame) + #[argh(option)] + pub compute_md5: Option, + + /// path to JSON file containing golden MD5 sums of each frame. + #[argh(option)] + pub golden: Option, +} + +/// Decide the output file name when multiple_output_files is set +pub fn decide_output_file_name<'a>(output: &'a Path, index: i32) -> PathBuf { + let extract_str = |s: Option<&'a OsStr>| s.and_then(|s| s.to_str()).expect("malformed file"); + + let [file_name, stem] = [output.file_name(), output.file_stem()].map(extract_str); + + if output.extension().is_some() { + let [extension] = [output.extension()].map(extract_str); + let new_file_name = format!("{}_{}.{}", stem, index, extension); + PathBuf::from(String::from(output.to_str().unwrap()).replace(file_name, &new_file_name)) + } else { + let new_file_name = format!("{}_{}", stem, index); + PathBuf::from(String::from(output.to_str().unwrap()).replace(file_name, &new_file_name)) + } +} + +// Vector of per frame md5 sums +pub fn golden_md5s(path: &Option) -> Vec { + let golden_md5s: Vec = match path { + None => vec![], + Some(ref path) => { + let mut golden_file_content = String::new(); + File::open(path) + .expect("error opening golden file") + .read_to_string(&mut golden_file_content) + .expect("error reading golden file"); + let parsed_json: serde_json::Value = + serde_json::from_str(&golden_file_content).expect("error parsing golden file"); + match &parsed_json["md5_checksums"] { + serde_json::Value::Array(checksums) => checksums + .iter() + .map(|x| match x { + serde_json::Value::String(checksum) => String::from(checksum), + _ => panic!("error parsing golden file"), + }) + .collect(), + _ => panic!("error parsing golden file"), + } + } + }; + golden_md5s +} diff --git a/vendor/cros-codecs/examples/ccenc/main.rs b/vendor/cros-codecs/examples/ccenc/main.rs new file mode 100644 index 00000000..0ea594ed --- /dev/null +++ b/vendor/cros-codecs/examples/ccenc/main.rs @@ -0,0 +1,314 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::fs::File; +use std::io::ErrorKind; +use std::io::Read; +use std::io::Write; +use std::path::PathBuf; +use std::sync::atomic::AtomicU32; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; +use std::time::Duration; + +use cros_codecs::bitstream_utils::IvfFileHeader; +use cros_codecs::bitstream_utils::IvfFrameHeader; +use cros_codecs::c2_wrapper::c2_encoder::C2EncoderWorker; +#[cfg(feature = "v4l2")] +use cros_codecs::c2_wrapper::c2_v4l2_encoder::C2V4L2Encoder; +#[cfg(feature = "v4l2")] +use cros_codecs::c2_wrapper::c2_v4l2_encoder::C2V4L2EncoderOptions; +#[cfg(feature = "vaapi")] +use cros_codecs::c2_wrapper::c2_vaapi_encoder::C2VaapiEncoder; +#[cfg(feature = "vaapi")] +use cros_codecs::c2_wrapper::c2_vaapi_encoder::C2VaapiEncoderOptions; +use cros_codecs::c2_wrapper::C2EncodeJob; +use cros_codecs::c2_wrapper::C2Status; +use cros_codecs::c2_wrapper::C2Worker; +use cros_codecs::c2_wrapper::C2Wrapper; +use cros_codecs::c2_wrapper::DrainMode; +use cros_codecs::decoder::StreamInfo; +use cros_codecs::image_processing::extend_border_nv12; +use cros_codecs::image_processing::i420_to_nv12_chroma; +use cros_codecs::image_processing::nv12_copy; +use cros_codecs::video_frame::frame_pool::FramePool; +use cros_codecs::video_frame::frame_pool::PooledVideoFrame; +use cros_codecs::video_frame::gbm_video_frame::GbmDevice; +use cros_codecs::video_frame::gbm_video_frame::GbmUsage; +use cros_codecs::video_frame::generic_dma_video_frame::GenericDmaVideoFrame; +use cros_codecs::video_frame::VideoFrame; +use cros_codecs::video_frame::UV_PLANE; +use cros_codecs::video_frame::Y_PLANE; +use cros_codecs::DecodedFormat; +use cros_codecs::Fourcc; +use cros_codecs::Resolution; + +mod util; + +use crate::util::Args; +use crate::util::Codec; + +// We use pooled video frames here because it prevents us from exhausting the FD limit on the +// machine. Unfortunately this does require us to cap the pipeline depth to the frame pool size. +const PIPELINE_DEPTH: usize = 64; + +fn codec_to_fourcc(codec: &Codec) -> Fourcc { + match codec { + Codec::H264 => Fourcc::from(b"H264"), + Codec::VP8 => Fourcc::from(b"VP80"), + Codec::VP9 => Fourcc::from(b"VP90"), + _ => panic!("Unsupported format!"), + } +} + +fn codec_to_ivf_magic(codec: &Codec) -> [u8; 4] { + match codec { + // Note that H264 does not generally use IVF containers. + Codec::VP8 => IvfFileHeader::CODEC_VP8, + Codec::VP9 => IvfFileHeader::CODEC_VP9, + _ => panic!("Unsupported format!"), + } +} + +fn enqueue_work( + encoder: &mut C2Wrapper>, W>, + file: &mut File, + framepool: Arc>>, + input_format: DecodedFormat, + input_coded_resolution: Resolution, + num_frames: u64, + bitrate: u64, + framerate: u32, + timestamp: &mut u64, +) -> bool +where + W: C2Worker>>, +{ + assert!(input_coded_resolution.width % 2 == 0); + assert!(input_coded_resolution.height % 2 == 0); + + if *timestamp >= num_frames { + if *timestamp == num_frames { + encoder.drain(DrainMode::EOSDrain); + *timestamp += 1; + } + return false; + } + + let mut new_frame = match (*framepool.lock().unwrap()).alloc() { + Some(frame) => frame, + // We've exhausted the pipeline depth + None => return false, + }; + + let mut buf = vec![0u8; input_coded_resolution.get_area() * 3 / 2]; + match file.read_exact(buf.as_mut_slice()) { + Ok(_) => (), + Err(e) => { + if e.kind() == ErrorKind::UnexpectedEof { + // We've reached the end of the input file, start draining. + encoder.drain(DrainMode::EOSDrain); + *timestamp = u64::MAX; + return false; + } else { + panic!("Error reading input file! {:?}", e); + } + } + } + + let input_y = &buf[0..input_coded_resolution.get_area()]; + let mut tmp_input_uv: Vec = Vec::new(); + let input_uv = match input_format { + DecodedFormat::NV12 => { + &buf[input_coded_resolution.get_area()..(input_coded_resolution.get_area() * 3 / 2)] + } + DecodedFormat::I420 => { + tmp_input_uv.resize(input_coded_resolution.get_area() / 2, 0); + let input_u = &buf + [input_coded_resolution.get_area()..(input_coded_resolution.get_area() * 5 / 4)]; + let input_v = &buf[(input_coded_resolution.get_area() * 5 / 4) + ..(input_coded_resolution.get_area() * 3 / 2)]; + i420_to_nv12_chroma(input_u, input_v, tmp_input_uv.as_mut_slice()); + tmp_input_uv.as_slice() + } + _ => panic!("Unsupported input format!"), + }; + + { + let visible_resolution = new_frame.resolution(); + let dst_pitches = new_frame.get_plane_pitch(); + let dst_sizes = new_frame.get_plane_size(); + let coded_resolution = Resolution { + width: dst_pitches[0] as u32, + height: (dst_sizes[0] / dst_pitches[0]) as u32, + }; + let dst_mapping = new_frame.map_mut().expect("Failed to map input frame!"); + let dst_planes = dst_mapping.get(); + nv12_copy( + input_y, + input_coded_resolution.width as usize, + *dst_planes[Y_PLANE].borrow_mut(), + dst_pitches[Y_PLANE], + input_uv, + input_coded_resolution.width as usize, + *dst_planes[UV_PLANE].borrow_mut(), + dst_pitches[UV_PLANE], + visible_resolution.width as usize, + visible_resolution.height as usize, + ); + extend_border_nv12( + *dst_planes[Y_PLANE].borrow_mut(), + *dst_planes[UV_PLANE].borrow_mut(), + visible_resolution.width as usize, + visible_resolution.height as usize, + coded_resolution.width as usize, + coded_resolution.height as usize, + ); + } + + let job = C2EncodeJob { + input: Some(new_frame), + output: vec![], + timestamp: *timestamp, + bitrate: bitrate, + framerate: Arc::new(AtomicU32::new(framerate)), + drain: DrainMode::NoDrain, + }; + encoder.queue(vec![job]); + + *timestamp += 1; + + true +} + +fn main() { + env_logger::init(); + + let args: Args = argh::from_env(); + + let mut input = File::open(&args.input).expect("error opening input file!"); + + let input_fourcc = Fourcc::from(b"NV12"); + let codec = args.codec.unwrap_or_default(); + let output_fourcc = codec_to_fourcc(&codec); + + let gbm_device = Arc::new( + GbmDevice::open(PathBuf::from("/dev/dri/renderD128")).expect("Could not open GBM device!"), + ); + let framepool = Arc::new(Mutex::new(FramePool::new(move |stream_info: &StreamInfo| { + as Clone>::clone(&gbm_device) + .new_frame( + Fourcc::from(b"NV12"), + stream_info.display_resolution, + stream_info.coded_resolution, + GbmUsage::Encode, + ) + .expect("Could not allocate frame for frame pool!") + .to_generic_dma_video_frame() + .expect("Could not export GBM frame to DMA frame!") + }))); + let framepool_ = framepool.clone(); + let framepool_hint_cb = move |stream_info: StreamInfo| { + (*framepool_.lock().unwrap()).resize(&StreamInfo { + format: stream_info.format, + coded_resolution: stream_info.coded_resolution, + display_resolution: stream_info.display_resolution, + min_num_frames: PIPELINE_DEPTH, + }); + }; + + // We shouldn't need temp frames for GBM. + let alloc_cb = move || None; + + let error_cb = move |_status: C2Status| { + panic!("Unrecoverable encoding error!"); + }; + + let output_file = Arc::new(Mutex::new( + File::create(args.output.unwrap()).expect("Error creating output file"), + )); + if codec != Codec::H264 { + let hdr = IvfFileHeader::new( + codec_to_ivf_magic(&codec), + args.width as u16, + args.height as u16, + args.framerate, + args.count as u32, + ); + hdr.writo_into(&mut *output_file.lock().unwrap()).expect("Error writing IVF file header!"); + } + let codec_ = codec.clone(); + let work_done_cb = move |job: C2EncodeJob>| { + if codec_ != Codec::H264 { + let hdr = + IvfFrameHeader { timestamp: job.timestamp, frame_size: job.output.len() as u32 }; + hdr.writo_into(&mut *output_file.lock().unwrap()) + .expect("Error writing IVF frame header!"); + } + let _ = (*output_file.lock().unwrap()) + .write(job.output.as_slice()) + .expect("Error writing output file!"); + }; + + let input_coded_resolution = Resolution { + width: args.coded_width.unwrap_or(args.width), + height: args.coded_height.unwrap_or(args.height), + }; + assert!(input_coded_resolution.width >= args.width); + assert!(input_coded_resolution.height >= args.height); + + #[cfg(feature = "v4l2")] + let mut encoder: C2Wrapper<_, C2EncoderWorker<_, C2V4L2Encoder>> = C2Wrapper::new( + input_fourcc, + output_fourcc.clone(), + error_cb, + work_done_cb, + framepool_hint_cb, + alloc_cb, + C2V4L2EncoderOptions { + output_fourcc: output_fourcc, + visible_resolution: Resolution { width: args.width, height: args.height }, + }, + ); + #[cfg(feature = "vaapi")] + let mut encoder: C2Wrapper<_, C2EncoderWorker<_, C2VaapiEncoder>> = C2Wrapper::new( + input_fourcc, + output_fourcc.clone(), + error_cb, + work_done_cb, + framepool_hint_cb, + alloc_cb, + C2VaapiEncoderOptions { + low_power: args.low_power, + visible_resolution: Resolution { width: args.width, height: args.height }, + }, + ); + + let mut timestamp: u64 = 0; + + // Start the encoder + encoder.start(); + + // Run the encode job + while encoder.is_alive() { + // Enqueue as much as we can until the framepool is exhausted + while enqueue_work( + &mut encoder, + &mut input, + framepool.clone(), + args.fourcc, + input_coded_resolution.clone(), + args.count as u64, + args.bitrate, + args.framerate, + &mut timestamp, + ) {} + + thread::sleep(Duration::from_millis(10)); + } + + // Shut down the encoder + encoder.stop(); +} diff --git a/vendor/cros-codecs/examples/ccenc/util.rs b/vendor/cros-codecs/examples/ccenc/util.rs new file mode 100644 index 00000000..ab7558af --- /dev/null +++ b/vendor/cros-codecs/examples/ccenc/util.rs @@ -0,0 +1,87 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::path::PathBuf; +use std::str::FromStr; + +use argh::FromArgs; + +use cros_codecs::DecodedFormat; + +#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)] +pub enum Codec { + #[default] + H264, + H265, + VP8, + VP9, + AV1, +} + +impl FromStr for Codec { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "h264" | "H264" => Ok(Self::H264), + "h265" | "H265" => Ok(Self::H265), + "vp8" | "VP8" => Ok(Self::VP8), + "vp9" | "VP9" => Ok(Self::VP9), + "av1" | "AV1" => Ok(Self::AV1), + _ => Err("unrecognized codec. Valid values: h264, h265, vp8, vp9, av1"), + } + } +} + +/// Simple encoder +#[derive(Debug, FromArgs)] +pub struct Args { + /// input file + #[argh(positional)] + pub input: PathBuf, + + /// input frames width + #[argh(option)] + pub width: u32, + + /// input frames height + #[argh(option)] + pub height: u32, + + /// input frame coded width + #[argh(option)] + pub coded_width: Option, + + /// input frame coded height + #[argh(option)] + pub coded_height: Option, + + /// input frames count + #[argh(option)] + pub count: usize, + + /// input fourcc + #[argh(option)] + pub fourcc: DecodedFormat, + + /// codec + #[argh(option)] + pub codec: Option, + + /// framerate + #[argh(option, default = "30")] + pub framerate: u32, + + /// bitrate + #[argh(option, default = "200000")] + pub bitrate: u64, + + /// output file to write the decoded frames to + #[argh(option)] + pub output: Option, + + /// set to true if low power version of the API shall be used + #[argh(switch)] + pub low_power: bool, +} diff --git a/vendor/cros-codecs/examples/perf_test.rs b/vendor/cros-codecs/examples/perf_test.rs new file mode 100644 index 00000000..d65d9e1c --- /dev/null +++ b/vendor/cros-codecs/examples/perf_test.rs @@ -0,0 +1,135 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use cros_codecs::image_processing::*; +use std::ptr; +use std::time::{Duration, Instant}; + +const K_NUMBER_OF_TEST_CYCLES: u64 = 2000; +const K_ADDR_ALIGN: usize = 16; +const K_DATA_LEN: usize = 480 * 288 * 3 / 2; + +#[cfg(feature = "v4l2")] +fn test_mm21_to_nv12_perf() { + let test_input = include_bytes!("../src/test_data/puppets-480x270_20230825.mm21.yuv"); + let test_expected_output = include_bytes!("../src/test_data/puppets-480x270_20230825.nv12.yuv"); + + // Byte align test input. + let mut aligned_input = [0u8; (K_DATA_LEN + K_ADDR_ALIGN)]; + let mut index = K_ADDR_ALIGN - (ptr::addr_of!(aligned_input[0]) as usize % K_ADDR_ALIGN); + let input_data = aligned_input.get_mut(index..(index + K_DATA_LEN)).unwrap(); + input_data.copy_from_slice(test_input); + + // Byte align test output. + let mut aligned_output = [0u8; (K_DATA_LEN + K_ADDR_ALIGN)]; + index = K_ADDR_ALIGN - (ptr::addr_of!(aligned_output[0]) as usize % K_ADDR_ALIGN); + let output_data = aligned_output.get_mut(index..(index + K_DATA_LEN)).unwrap(); + let (test_y_output, test_uv_output) = output_data.split_at_mut(480 * 288); + + let start_time = Instant::now(); + for _cycle in 0..K_NUMBER_OF_TEST_CYCLES { + let _ = mm21_to_nv12( + &input_data[0..480 * 288], + 480, + test_y_output, + 480, + &input_data[480 * 288..480 * 288 * 3 / 2], + 480, + test_uv_output, + 480, + 480, + 288, + ); + } + + let duration_us = ((Instant::now() - start_time).as_micros()) as f64; + + let fps = K_NUMBER_OF_TEST_CYCLES as f64 / (duration_us / 1000000f64); + + println!("MM21 to NV12 Perf Test Results"); + println!("-----------------------------------"); + println!("Frames Decoded: {}", K_NUMBER_OF_TEST_CYCLES); + println!("TotalDurationMs: {}", duration_us); + println!("FramesPerSecond: {}\n\n", fps); + + assert_eq!(output_data, *test_expected_output); +} + +fn test_nv12_to_i420_perf() { + let test_input = include_bytes!("../src/test_data/puppets-480x270_20230825.nv12.yuv"); + let test_expected_output = include_bytes!("../src/test_data/puppets-480x270_20230825.i420.yuv"); + + let mut test_output = [0u8; 480 * 288 * 3 / 2]; + let (test_y_output, test_uv_output) = test_output.split_at_mut(480 * 288); + let (test_u_output, test_v_output) = test_uv_output.split_at_mut(480 * 288 / 4); + + let start_time = Instant::now(); + for _cycle in 0..K_NUMBER_OF_TEST_CYCLES { + let _ = nv12_to_i420( + &test_input[0..480 * 288], + 480, + test_y_output, + 480, + &test_input[480 * 288..480 * 288 * 3 / 2], + 480, + test_u_output, + 240, + test_v_output, + 240, + 480, + 288, + ); + } + + let duration_us = ((Instant::now() - start_time).as_micros()) as f64; + + let fps = K_NUMBER_OF_TEST_CYCLES as f64 / (duration_us / 1000000f64); + + println!("NV12 to I420 Perf Test Results"); + println!("-----------------------------------"); + println!("Frames Decoded: {}", K_NUMBER_OF_TEST_CYCLES); + println!("TotalDurationMs: {}", duration_us); + println!("FramesPerSecond: {}\n\n", fps); + + assert_eq!(test_output, *test_expected_output); +} + +fn test_i420_to_nv12_perf() { + let test_input = include_bytes!("../src/test_data/puppets-480x270_20230825.i420.yuv"); + let test_expected_output = include_bytes!("../src/test_data/puppets-480x270_20230825.nv12.yuv"); + + let mut test_output = [0u8; 480 * 288 * 3 / 2]; + let (test_y_output, test_uv_output) = test_output.split_at_mut(480 * 288); + + let start_time = Instant::now(); + for _cycle in 0..K_NUMBER_OF_TEST_CYCLES { + let _ = i420_to_nv12( + &test_input[0..(480 * 288)], + test_y_output, + &test_input[(480 * 288)..(480 * 288 * 5 / 4)], + &test_input[(480 * 288 * 5 / 4)..(480 * 288 * 3 / 2)], + test_uv_output, + ); + } + + let duration_us = ((Instant::now() - start_time).as_micros()) as f64; + + let fps = K_NUMBER_OF_TEST_CYCLES as f64 / (duration_us / 1000000f64); + + println!("I420 to NV12 Perf Test Results"); + println!("-----------------------------------"); + println!("Frames Decoded: {}", K_NUMBER_OF_TEST_CYCLES); + println!("TotalDurationMs: {}", duration_us); + println!("FramesPerSecond: {}\n\n", fps); + + assert_eq!(test_output, *test_expected_output); +} + +fn main() { + #[cfg(feature = "v4l2")] + test_mm21_to_nv12_perf(); + + test_nv12_to_i420_perf(); + test_i420_to_nv12_perf(); +} diff --git a/vendor/cros-codecs/rustfmt.toml b/vendor/cros-codecs/rustfmt.toml new file mode 100644 index 00000000..a70acca9 --- /dev/null +++ b/vendor/cros-codecs/rustfmt.toml @@ -0,0 +1,3 @@ +edition = "2021" +use_small_heuristics = "Max" +newline_style = "Unix" diff --git a/vendor/cros-codecs/simple_test.py b/vendor/cros-codecs/simple_test.py new file mode 100644 index 00000000..7ed741f8 --- /dev/null +++ b/vendor/cros-codecs/simple_test.py @@ -0,0 +1,46 @@ +import glob +import os +import subprocess + +CCDEC_BINARY_PATH = 'target/debug/examples/ccdec' + +def validate_decode(bitstream_path, codec): + if not os.path.isfile(bitstream_path + '.md5'): # Missing golden, skip this vector + return + + with open(bitstream_path + '.md5', 'r') as golden_file: + golden = golden_file.read() + + ccdec_process = subprocess.run([CCDEC_BINARY_PATH, bitstream_path, '--frame-memory', 'prime', '--input-format', codec, '--output-format', 'nv12', '--compute-md5', 'frame'], capture_output=True) + if ccdec_process.returncode != 0: + print('Error running ccdec for bitstream ' + bitstream_path) + print(ccdec_process.stderr.decode('utf-8')) + return + + test_output = ccdec_process.stdout.decode('utf-8') + if test_output != golden: + print('MD5 mistmatch on bitstream ' + bitstream_path) + test_lines = test_output.split('\n') + golden_lines = golden.split('\n') + if len(test_lines) > len(golden_lines): + print('Found more frames in ccdec output than expected') + elif len(test_lines) < len(golden_lines): + print('Found fewer frames in ccdec output than expected') + else: + for i in range(0, len(test_lines)): + if test_lines[i] != golden_lines[i]: + print('First mismatch on frame ' + str(i)) + print('Expected: ' + golden_lines[i]) + print('Got: ' + test_lines[i]) + break + + +if not os.path.isfile(CCDEC_BINARY_PATH): + print('Error! ccdec not found. Have you compiled examples yet?') + +extensions = ['.h264', '.h265', '.av1', '.vp8', '.vp9', '.ivf'] +for extension in extensions: + for bitstream_path in glob.glob('src/codec/*/test_data/*' + extension): + codec = bitstream_path[len('src/codec/'):] + codec = codec[:codec.find('/')] + validate_decode(bitstream_path, codec) diff --git a/vendor/cros-codecs/src/backend.rs b/vendor/cros-codecs/src/backend.rs new file mode 100644 index 00000000..d54fb92c --- /dev/null +++ b/vendor/cros-codecs/src/backend.rs @@ -0,0 +1,16 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! Shared code for codec backends. +//! +//! A backend is a provider of codec decoding or encoding, most likely hardware-accelerated like +//! VAAPI. This module contains backend-related code that is not tied to any particular codec and +//! can be shared between various parts of this crate. + +#[cfg(any(test, fuzzing))] +pub(crate) mod dummy; +#[cfg(feature = "v4l2")] +pub mod v4l2; +#[cfg(feature = "vaapi")] +pub mod vaapi; diff --git a/vendor/cros-codecs/src/backend/dummy.rs b/vendor/cros-codecs/src/backend/dummy.rs new file mode 100644 index 00000000..da6fd986 --- /dev/null +++ b/vendor/cros-codecs/src/backend/dummy.rs @@ -0,0 +1,8 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! This file contains a dummy backends whose only purpose is to let the codec +//! run so we can test it in isolation. + +pub(crate) mod decoder; diff --git a/vendor/cros-codecs/src/backend/dummy/decoder.rs b/vendor/cros-codecs/src/backend/dummy/decoder.rs new file mode 100644 index 00000000..00b3610b --- /dev/null +++ b/vendor/cros-codecs/src/backend/dummy/decoder.rs @@ -0,0 +1,149 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! This file contains a dummy backend whose only purpose is to let the decoder +//! run so we can test it in isolation. + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::decoder::stateless::PoolLayer; +use crate::decoder::stateless::StatelessCodec; +use crate::decoder::stateless::StatelessDecoderBackend; +use crate::decoder::stateless::StatelessDecoderBackendPicture; +use crate::decoder::stateless::TryFormat; +use crate::decoder::DecodedHandle; +use crate::decoder::DynHandle; +use crate::decoder::FramePool; +use crate::decoder::MappableHandle; +use crate::decoder::StreamInfo; +use crate::DecodedFormat; +use crate::Resolution; + +#[derive(Default)] +pub struct BackendHandle(()); + +impl MappableHandle for BackendHandle { + fn read(&mut self, _: &mut [u8]) -> anyhow::Result<()> { + Ok(()) + } + + fn image_size(&mut self) -> usize { + 1 + } +} + +impl<'a> DynHandle for std::cell::RefMut<'a, BackendHandle> { + fn dyn_mappable_handle<'b>(&'b mut self) -> anyhow::Result> { + Ok(Box::::default()) + } +} + +pub struct Handle { + pub handle: Rc>, +} + +impl Clone for Handle { + fn clone(&self) -> Self { + Self { handle: Rc::clone(&self.handle) } + } +} + +impl DecodedHandle for Handle { + type Descriptor = (); + + fn coded_resolution(&self) -> Resolution { + Default::default() + } + + fn display_resolution(&self) -> Resolution { + Default::default() + } + + fn timestamp(&self) -> u64 { + 0 + } + + fn dyn_picture<'a>(&'a self) -> Box { + Box::new(self.handle.borrow()) + } + + fn sync(&self) -> anyhow::Result<()> { + Ok(()) + } + + fn is_ready(&self) -> bool { + true + } + + fn resource(&self) -> std::cell::Ref<()> { + std::cell::Ref::map(self.handle.borrow(), |h| &h.0) + } +} + +/// Dummy backend that can be used for any codec. +pub struct Backend { + stream_info: StreamInfo, +} + +impl Backend { + pub(crate) fn new() -> Self { + Self { + stream_info: StreamInfo { + format: DecodedFormat::I420, + min_num_frames: 4, + coded_resolution: Resolution::from((320, 200)), + display_resolution: Resolution::from((320, 200)), + }, + } + } +} + +impl FramePool for Backend { + type Descriptor = (); + + fn coded_resolution(&self) -> Resolution { + Resolution::from((320, 200)) + } + + fn set_coded_resolution(&mut self, _resolution: Resolution) {} + + fn add_frames(&mut self, _descriptors: Vec) -> Result<(), anyhow::Error> { + Ok(()) + } + + fn num_free_frames(&self) -> usize { + 4 + } + + fn num_managed_frames(&self) -> usize { + 4 + } + + fn clear(&mut self) {} +} + +impl StatelessDecoderBackendPicture for Backend { + type Picture = (); +} + +impl TryFormat for Backend { + fn try_format(&mut self, _: &Codec::FormatInfo, _: DecodedFormat) -> anyhow::Result<()> { + Ok(()) + } +} + +impl StatelessDecoderBackend for Backend { + type Handle = Handle; + + type FramePool = Self; + + fn stream_info(&self) -> Option<&StreamInfo> { + Some(&self.stream_info) + } + + fn frame_pool(&mut self, _: PoolLayer) -> Vec<&mut Self::FramePool> { + vec![self] + } +} diff --git a/vendor/cros-codecs/src/backend/v4l2.rs b/vendor/cros-codecs/src/backend/v4l2.rs new file mode 100644 index 00000000..9227ee78 --- /dev/null +++ b/vendor/cros-codecs/src/backend/v4l2.rs @@ -0,0 +1,20 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! V4L2 backend + +pub mod decoder; +pub mod encoder; + +impl From for crate::Fourcc { + fn from(value: v4l2r::PixelFormat) -> Self { + crate::Fourcc(value.to_u32()) + } +} + +impl From for v4l2r::PixelFormat { + fn from(value: crate::Fourcc) -> Self { + v4l2r::PixelFormat::from_u32(value.0) + } +} diff --git a/vendor/cros-codecs/src/backend/v4l2/decoder.rs b/vendor/cros-codecs/src/backend/v4l2/decoder.rs new file mode 100644 index 00000000..9adafbc3 --- /dev/null +++ b/vendor/cros-codecs/src/backend/v4l2/decoder.rs @@ -0,0 +1,21 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[cfg(feature = "v4l2")] +use crate::Rect; +use crate::Resolution; + +pub const ADDITIONAL_REFERENCE_FRAME_BUFFER: usize = 4; + +pub mod stateless; + +pub trait V4l2StreamInfo { + /// Returns the minimum number of surfaces required to decode the stream. + // name was chosen to match vaapi + fn min_num_frames(&self) -> usize; + /// Returns the coded size of the surfaces required to decode the stream. + fn coded_size(&self) -> Resolution; + /// Returns the visible rectangle within the coded size for the stream. + fn visible_rect(&self) -> Rect; +} diff --git a/vendor/cros-codecs/src/backend/v4l2/decoder/stateless.rs b/vendor/cros-codecs/src/backend/v4l2/decoder/stateless.rs new file mode 100644 index 00000000..d3307d23 --- /dev/null +++ b/vendor/cros-codecs/src/backend/v4l2/decoder/stateless.rs @@ -0,0 +1,154 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Arc; + +use crate::backend::v4l2::decoder::V4l2StreamInfo; +use crate::decoder::stateless::NewStatelessDecoderError; +use crate::decoder::stateless::StatelessBackendResult; +use crate::decoder::stateless::StatelessDecoderBackend; +use crate::decoder::DecodedHandle; +use crate::decoder::StreamInfo; +use crate::device::v4l2::stateless::device::V4l2Device; +use crate::device::v4l2::stateless::request::V4l2Request; +use crate::video_frame::VideoFrame; +use crate::DecodedFormat; +use crate::Fourcc; +use crate::Resolution; + +pub struct V4l2Picture { + request: Rc>>, + // To properly decode stream while output and capture queues + // are processed independently it's required for v4l2 backend + // to maintain DPB buffer recycling. The following vector + // is used to prevent reference pictures to be reused while + // current picture is still being decoded. + ref_pictures: Option>>>>, +} + +impl V4l2Picture { + pub fn new(request: Rc>>) -> Self { + Self { request, ref_pictures: None } + } + pub fn video_frame(&self) -> Arc { + self.request.as_ref().borrow().result().capture_buffer.borrow().frame.clone() + } + pub fn timestamp(&self) -> u64 { + self.request.as_ref().borrow().timestamp() + } + pub fn set_ref_pictures( + &mut self, + ref_pictures: Vec>>>, + ) -> &mut Self { + self.ref_pictures = Some(ref_pictures); + self + } + pub fn sync(&mut self) -> &mut Self { + self.request.as_ref().borrow_mut().sync(); + self.ref_pictures = None; + self + } + pub fn request(&mut self) -> Rc>> { + self.request.clone() + } + pub fn drop_references(&mut self) { + self.ref_pictures = None; + } +} + +pub struct V4l2StatelessDecoderHandle { + pub picture: Rc>>, + pub stream_info: StreamInfo, +} + +impl Clone for V4l2StatelessDecoderHandle { + fn clone(&self) -> Self { + Self { picture: Rc::clone(&self.picture), stream_info: self.stream_info.clone() } + } +} + +impl DecodedHandle for V4l2StatelessDecoderHandle { + type Frame = V; + + fn video_frame(&self) -> Arc { + self.picture.borrow().video_frame() + } + + fn coded_resolution(&self) -> Resolution { + self.stream_info.coded_resolution.clone() + } + + fn display_resolution(&self) -> Resolution { + self.stream_info.display_resolution.clone() + } + + fn timestamp(&self) -> u64 { + self.picture.borrow().timestamp() + } + + fn sync(&self) -> anyhow::Result<()> { + self.picture.borrow_mut().sync(); + Ok(()) + } + + fn is_ready(&self) -> bool { + todo!(); + } +} + +pub struct V4l2StatelessDecoderBackend { + pub device: V4l2Device, + pub stream_info: StreamInfo, + pub frame_counter: u64, +} + +impl V4l2StatelessDecoderBackend { + pub fn new() -> Result { + Ok(Self { + device: V4l2Device::new()?, + stream_info: StreamInfo { + format: DecodedFormat::I420, + min_num_frames: 0, + coded_resolution: Resolution::from((0, 0)), + display_resolution: Resolution::from((0, 0)), + }, + frame_counter: 0, + }) + } + + pub(crate) fn new_sequence( + &mut self, + stream_params: &StreamData, + fourcc: Fourcc, + ) -> StatelessBackendResult<()> + where + for<'a> &'a StreamData: V4l2StreamInfo, + { + let coded_resolution = stream_params.coded_size().clone(); + let min_num_frames = stream_params.min_num_frames(); + + // TODO: Query the driver for the format + self.stream_info.format = DecodedFormat::MM21; + self.stream_info.display_resolution = Resolution::from(stream_params.visible_rect()); + self.stream_info.coded_resolution = coded_resolution; + self.stream_info.min_num_frames = min_num_frames; + + Ok(self.device.initialize_queues(fourcc, coded_resolution, min_num_frames as u32)?) + } +} + +impl StatelessDecoderBackend for V4l2StatelessDecoderBackend { + type Handle = V4l2StatelessDecoderHandle; + + fn stream_info(&self) -> Option<&StreamInfo> { + // TODO + Some(&self.stream_info) + } + + fn reset_backend(&mut self) -> anyhow::Result<()> { + Ok(self.device.reset_queues()?) + } +} diff --git a/vendor/cros-codecs/src/backend/v4l2/encoder.rs b/vendor/cros-codecs/src/backend/v4l2/encoder.rs new file mode 100644 index 00000000..80898215 --- /dev/null +++ b/vendor/cros-codecs/src/backend/v4l2/encoder.rs @@ -0,0 +1,1368 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::collections::BTreeMap; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::os::fd::AsRawFd; +use std::path::PathBuf; +use std::sync::Arc; + +use nix::sys::stat::fstat; +use thiserror::Error; +use v4l2r::bindings::v4l2_streamparm; +use v4l2r::controls::codec::VideoBitrate; +use v4l2r::controls::codec::VideoBitrateMode; +use v4l2r::controls::codec::VideoConstantQuality; +use v4l2r::controls::codec::VideoForceKeyFrame; +use v4l2r::controls::codec::VideoHeaderMode; +use v4l2r::controls::ExtControlTrait; +use v4l2r::controls::SafeExtControl; +use v4l2r::device::poller::DeviceEvent; +use v4l2r::device::poller::PollError; +use v4l2r::device::poller::Poller; +use v4l2r::device::queue::direction::Capture; +use v4l2r::device::queue::direction::Output; +use v4l2r::device::queue::dqbuf::DqBuffer; +use v4l2r::device::queue::qbuf::QBuffer; +use v4l2r::device::queue::BuffersAllocated; +use v4l2r::device::queue::CreateQueueError; +use v4l2r::device::queue::GetFreeBufferError; +use v4l2r::device::queue::GetFreeCaptureBuffer; +use v4l2r::device::queue::GetFreeOutputBuffer; +use v4l2r::device::queue::OutputQueueable; +use v4l2r::device::queue::OutputQueueableProvider; +use v4l2r::device::queue::Queue; +use v4l2r::device::queue::RequestBuffersError; +use v4l2r::device::AllocatedQueue; +use v4l2r::device::Device; +use v4l2r::device::DeviceConfig; +use v4l2r::device::Stream; +use v4l2r::device::TryDequeue; +use v4l2r::ioctl; +use v4l2r::ioctl::BufferFlags; +use v4l2r::ioctl::EncoderCommand; +use v4l2r::ioctl::StreamOnError; +use v4l2r::ioctl::V4l2BufferFromError; +use v4l2r::memory::BufferHandles; +use v4l2r::memory::DmaBufHandle; +use v4l2r::memory::MmapHandle; +use v4l2r::memory::PlaneHandle; +use v4l2r::memory::PrimitiveBufferHandles; +use v4l2r::memory::UserPtrHandle; +use v4l2r::nix::errno::Errno; +use v4l2r::nix::sys::time::TimeVal; +use v4l2r::Format; +use v4l2r::PixelFormat; +use v4l2r::QueueDirection; +use v4l2r::QueueType; + +use crate::encoder::stateful::BackendOutput; +use crate::encoder::stateful::BackendRequest; +use crate::encoder::stateful::BackendRequestId; +use crate::encoder::stateful::StatefulBackendError; +use crate::encoder::stateful::StatefulBackendResult; +use crate::encoder::stateful::StatefulVideoEncoderBackend; +use crate::encoder::CodedBitstreamBuffer; +use crate::encoder::EncodeError; +use crate::encoder::FrameMetadata; +use crate::encoder::RateControl; +use crate::encoder::Tunings; +use crate::utils::DmabufFrame; +use crate::utils::UserPtrFrame; +use crate::video_frame::V4l2VideoFrame; +use crate::video_frame::VideoFrame; +use crate::Fourcc; +use crate::FrameLayout; +use crate::Resolution; + +#[derive(Debug, Error)] +pub enum UnsupportedError { + #[error("frame upscaling")] + FrameUpscaling, + + #[error("buffer lacking TIMESTAMP_COPY flag")] + NoTimestampCopyFlag, + + #[error("unsupported profile")] + Profile, +} + +#[derive(Debug, Error)] +pub enum InitializationError { + #[error(transparent)] + Unsupported(UnsupportedError), + + #[error("failed to create a CAPTURE queue: {0:?}")] + CaptureQueueCreate(CreateQueueError), + + #[error("failed to create a OUTPUT queue: {0:?}")] + OutputQueueCreate(CreateQueueError), + + #[error("failed to set format for CAPTURE: {0:?}")] + SetFormatCapture(ioctl::SFmtError), + + #[error("failed to set format for OUTPUT: {0:?}")] + SetFormatOutput(ioctl::SFmtError), + + #[error("failed to request CAPTURE buffers: {0:?}")] + RequestBufferCatpure(RequestBuffersError), + + #[error("failed to request OUTPUT buffers: {0:?}")] + RequestBufferOutput(RequestBuffersError), + + #[error("failed to stream on CAPTURE: {0:?}")] + StreamOnCapture(StreamOnError), + + #[error("failed to stream on OUTPUT: {0:?}")] + StreamOnOutput(StreamOnError), + + #[error(transparent)] + EncoderStart(#[from] ioctl::EncoderCmdError), + + #[error(transparent)] + CreatePoller(v4l2r::nix::Error), + + #[error(transparent)] + SetSelection(ioctl::SSelectionError), + + #[error(transparent)] + Contro(#[from] ControlError), +} + +#[derive(Debug, Error)] +pub struct ControlError { + which: &'static str, + error: Errno, +} + +impl std::fmt::Display for ControlError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("failed to set '{}': {:?}", self.which, self.error)) + } +} + +#[derive(Debug, Error)] +pub enum BackendError { + #[error(transparent)] + Unsupported(UnsupportedError), + + #[error(transparent)] + GetFreeBufferError(#[from] GetFreeBufferError), + + #[error(transparent)] + QueueBitstreamBuffer(anyhow::Error), + + #[error(transparent)] + MapBitstreamBuffer(anyhow::Error), + + #[error(transparent)] + QueueFrameHandleError(anyhow::Error), + + #[error(transparent)] + DequeueBuffer(#[from] ioctl::DqBufError), + + #[error("failed to map capture buffer: {0:?}")] + FailedToMapCapture(Timestamp), + + #[error(transparent)] + DrainCommand(#[from] ioctl::EncoderCmdError), + + #[error(transparent)] + Poll(#[from] PollError), + + #[error(transparent)] + GetFormat(#[from] ioctl::GFmtError), + + #[error(transparent)] + Control(#[from] ControlError), +} + +pub type BackendResult = std::result::Result; + +impl From for StatefulBackendError { + fn from(value: BackendError) -> Self { + StatefulBackendError::Other(anyhow::anyhow!(value)) + } +} + +impl From for EncodeError { + fn from(value: BackendError) -> Self { + EncodeError::StatefulBackendError(value.into()) + } +} + +/// Frame timestamp helper struct +#[repr(transparent)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Timestamp(pub u64); + +impl From for Timestamp { + fn from(value: v4l2r::bindings::timeval) -> Self { + let timestamp = value.tv_sec.wrapping_mul(1_000_000); + let timestamp = timestamp.wrapping_add(value.tv_usec); + Timestamp(timestamp.max(0) as u64) + } +} + +impl From<&Timestamp> for TimeVal { + fn from(value: &Timestamp) -> Self { + let tv_sec = (value.0 / 1_000_000).min(i64::MAX as u64); + let tv_usec = (value.0 % 1_000_000).min(i64::MAX as u64); + Self::new(tv_sec as i64, tv_usec as i64) + } +} + +pub type OutputBuffer<'a, P> = + > as OutputQueueableProvider<'a, P>>::Queueable; + +/// Encoder input frame handle, that can be queued to OUTPUT queue. +pub trait OutputBufferHandle { + type PrimitiveBufferHandles: PrimitiveBufferHandles; + + fn queue(self, buffer: OutputBuffer<'_, Self::PrimitiveBufferHandles>) -> anyhow::Result<()>; +} + +pub trait AlwaysEntireBufferUsed {} + +impl AlwaysEntireBufferUsed for UserPtrFrame {} + +impl AlwaysEntireBufferUsed for DmabufFrame {} + +impl AlwaysEntireBufferUsed for V4l2VideoFrame {} + +impl OutputBufferHandle for T +where + T: PrimitiveBufferHandles + AlwaysEntireBufferUsed, +{ + type PrimitiveBufferHandles = Self; + + fn queue(self, buffer: OutputBuffer<'_, Self>) -> anyhow::Result<()> { + let mut bytes_used = Vec::new(); + for i in 0..self.len() { + let mut plane = v4l2r::bindings::v4l2_plane::default(); + self.fill_v4l2_plane(i, &mut plane); + bytes_used.push(plane.length as usize); + } + + log::trace!("Queueing buffer bytes_used={bytes_used:?}"); + buffer.queue_with_handles(self, &bytes_used).unwrap(); + Ok(()) + } +} + +impl BufferHandles for UserPtrFrame { + type SupportedMemoryType = v4l2r::memory::MemoryType; + + fn fill_v4l2_plane(&self, index: usize, plane: &mut v4l2r::bindings::v4l2_plane) { + let plane_layout = &self.layout.planes[index]; + + plane.m.userptr = self.buffers[plane_layout.buffer_index] as _; + plane.data_offset = plane_layout.offset as _; + plane.length = self.mem_layout.size() as _; + } + + fn len(&self) -> usize { + self.layout.planes.len() + } +} + +impl PrimitiveBufferHandles for UserPtrFrame { + type HandleType = UserPtrHandle<[u8; 0]>; + const MEMORY_TYPE: Self::SupportedMemoryType = v4l2r::memory::MemoryType::UserPtr; +} + +// SAFETY: Access to the frame is read only +unsafe impl Send for UserPtrFrame {} + +// SAFETY: Access to the frame is read only +unsafe impl Sync for UserPtrFrame {} + +impl BufferHandles for DmabufFrame { + type SupportedMemoryType = v4l2r::memory::MemoryType; + + fn fill_v4l2_plane(&self, index: usize, plane: &mut v4l2r::bindings::v4l2_plane) { + let plane_layout = &self.layout.planes[index]; + let fd = &self.fds[plane_layout.buffer_index]; + + plane.m.fd = fd.as_raw_fd(); + plane.data_offset = plane_layout.offset as u32; + plane.length = fstat(fd.as_raw_fd()).map(|stat| stat.st_size as u32).unwrap_or(0); + + if plane.length == 0 { + log::warn!("Failed to fstat proper plane size index={index}"); + } + } + + fn len(&self) -> usize { + self.layout.planes.len() + } +} + +impl PrimitiveBufferHandles for DmabufFrame { + type HandleType = DmaBufHandle; + const MEMORY_TYPE: Self::SupportedMemoryType = v4l2r::memory::MemoryType::DmaBuf; +} + +/// Encoder's coded specific trait enabling setting codec specific tunings +pub trait EncoderCodec { + /// Set's [`Tunings`] for the [`v4l2r::device::Device`] + fn apply_tunings(device: &Device, tunings: &Tunings) -> Result<(), ControlError>; +} + +/// Trait responsible for CAPTURE buffers of the encoder's [`V4L2Backend`]. Enable custom logic of +/// CAPTURE specific for device/client use case. Useful especially when MMAP buffer type is not +/// supported for CAPTURE queue. In such scenario the client may choose to implement this function +/// and use own logic for allocating DMABUF or USERPTR. +pub trait CaptureBuffers { + /// [`PlaneHandle`] that is going to be used for CAPTURE buffers. + type PlaneHandle: PlaneHandle; + + /// Queues the buffer with [`CaptureBuffers::PlaneHandle`]s and returns true, + /// otherwise if the buffer may not be queue returns false. + fn queue( + &mut self, + buffer: QBuffer< + Capture, + Vec, + Vec, + &Queue>>, + >, + ) -> anyhow::Result; + + /// Maps the the buffer and returns its contents in form of [`Vec`] + fn export(&self, buffer: DqBuffer>) -> anyhow::Result>; +} + +/// [`CaptureBuffers`] implementation for MMAP memory type +pub struct MmapingCapture; + +impl CaptureBuffers for MmapingCapture { + type PlaneHandle = MmapHandle; + + fn queue( + &mut self, + buffer: QBuffer< + Capture, + Vec, + Vec, + &Queue>>, + >, + ) -> anyhow::Result { + buffer.queue()?; + Ok(true) + } + + fn export(&self, buffer: DqBuffer>) -> anyhow::Result> { + let timestamp = Timestamp::from(buffer.data.timestamp()); + let Some(mapping) = buffer.get_plane_mapping(0) else { + log::error!("CAPTURE: Failed to map buffer timestamp={timestamp:?}"); + return Err(BackendError::FailedToMapCapture(timestamp).into()); + }; + + let bytesused = *buffer.data.get_first_plane().bytesused as usize; + + Ok(Vec::from(&mapping.data[..bytesused])) + } +} + +/// V4L2 stateful encoder implementation +pub struct V4L2Backend +where + Handle: OutputBufferHandle, + CaptureBufferz: CaptureBuffers, + Self: EncoderCodec, +{ + /// V4L2 encoder device + device: Arc, + + /// OUTPUT_MPLANE V4L2 queue + output_queue: Queue>, + + /// CAPTURE_MPLANE V4L2 queue + capture_queue: Queue>>, + + /// [`CaptureBuffers`] implementation + capture_buffers: CaptureBufferz, + + /// Buffers that are currently processed by the encoder device + currently_processed: BTreeMap, + + /// Currently set [`Tunings`] used to detected tunings change + current_tunings: Tunings, + + /// Device poller for implementing [`StatefulVideoEncoderBackend::sync`] + poller: Poller, + + _phantom: PhantomData<(Handle, Codec)>, +} + +impl V4L2Backend +where + Handle: OutputBufferHandle, + CaptureBufferz: CaptureBuffers, + Self: EncoderCodec, +{ + /// Checks if the device has the given control and sets it to desired value if it's diffrent + pub(crate) fn apply_ctrl( + device: &Device, + name: &'static str, + value: C, + ) -> Result<(), ControlError> + where + C: ExtControlTrait + Into, + { + let mut current = SafeExtControl::::from_value(0); + + log::trace!("Trying to set control {name}"); + match ioctl::g_ext_ctrls(device, ioctl::CtrlWhich::Current, &mut current) { + Ok(()) => (), + Err(ioctl::ExtControlError { + error_idx: _, + error: ioctl::ExtControlErrorType::IoctlError(Errno::EINVAL), + }) => { + log::debug!("Setting/getting {name} control is not supported for this device"); + return Ok(()); + } + Err(ioctl::ExtControlError { + error_idx: _, + error: ioctl::ExtControlErrorType::IoctlError(error), + }) => { + log::error!("Getting {name} control returned {:?}", error.desc()); + return Err(ControlError { which: name, error }); + } + }; + + let desired: i32 = value.into(); + if current.value() == desired { + log::debug!("Control {name} already has desired value"); + } + + let mut value = SafeExtControl::::from_value(desired); + + match ioctl::s_ext_ctrls(device, ioctl::CtrlWhich::Current, &mut value) { + Ok(()) => (), + Err(ioctl::ExtControlError { + error_idx: _, + error: ioctl::ExtControlErrorType::IoctlError(Errno::EINVAL), + }) => { + log::debug!("Setting/getting {name} control is not supported for this device"); + return Ok(()); + } + Err(ioctl::ExtControlError { + error_idx: _, + error: ioctl::ExtControlErrorType::IoctlError(error), + }) => return Err(ControlError { which: name, error }), + }; + + let value = value.value(); + + if value != desired { + // TODO: raise error? + log::warn!("Failed to set desired {name} (to: {desired}, is: {value})",); + } else { + log::trace!("Control {name} set correctly to {value}"); + } + + Ok(()) + } + + /// Sets the frame rate using S_PARM ioctl for the queue type on the device. + pub(crate) fn apply_parm(device: &Device, queue_type: QueueType, framerate: u32) { + let mut parm = v4l2_streamparm { type_: queue_type as u32, ..Default::default() }; + + let (num, denum) = if framerate != 0 { (1, framerate) } else { (0, 1) }; + + if matches!(queue_type, v4l2r::QueueType::VideoOutputMplane) { + parm.parm.output.capability = 0; + parm.parm.output.outputmode = 0; + parm.parm.output.timeperframe.numerator = num; + parm.parm.output.timeperframe.denominator = denum; + } else { + parm.parm.capture.capability = 0; + parm.parm.capture.timeperframe.numerator = num; + parm.parm.capture.timeperframe.denominator = denum; + } + + match v4l2r::ioctl::s_parm::<_, v4l2_streamparm>(device, parm) { + Ok(parm) => match QueueType::n(parm.type_).as_ref().map(QueueType::direction) { + // SAFETY: The type is set to output + Some(QueueDirection::Output) => unsafe { + log::debug!( + "OUTPUT: Time per frame set to {}/{}", + parm.parm.output.timeperframe.numerator, + parm.parm.output.timeperframe.denominator, + ); + }, + // SAFETY: The type is set to capture + Some(QueueDirection::Capture) => unsafe { + log::debug!( + "CAPTURE: Time per frame set to {}/{}", + parm.parm.capture.timeperframe.numerator, + parm.parm.capture.timeperframe.denominator, + ); + }, + _ => {} + }, + Err(errno) => log::warn!("{:?}: Failed to set parm: {errno:?}", queue_type.direction()), + } + } + + /// Sets the rate mode and bitrate params on the device. + fn apply_rate_control( + device: &Device, + framerate: u32, + rate_control: &RateControl, + ) -> Result<(), ControlError> { + Self::apply_parm(device, QueueType::VideoOutputMplane, framerate); + Self::apply_parm(device, QueueType::VideoCaptureMplane, 1000); + + Self::apply_ctrl( + device, + "bitrate mode", + match rate_control { + RateControl::ConstantBitrate(_) => VideoBitrateMode::ConstantBitrate, + RateControl::ConstantQuality(_) => VideoBitrateMode::ConstantQuality, + }, + )?; + + if let Some(bitrate) = rate_control.bitrate_target() { + Self::apply_ctrl(device, "bitrate", VideoBitrate(bitrate as i32))?; + } + + if let RateControl::ConstantQuality(qp) = rate_control { + Self::apply_ctrl(device, "constant quality", VideoConstantQuality(*qp as i32))?; + } + + Ok(()) + } + + /// Sets the crop. + pub fn apply_selection( + device: &Device, + visible_size: Resolution, + ) -> Result<(), ioctl::SSelectionError> { + let rect = + v4l2r::Rect { left: 0, top: 0, width: visible_size.width, height: visible_size.height }; + + log::trace!( + "Trying to apply to selection to (left: {}, top: {}, width: {}, height: {})", + rect.left, + rect.top, + rect.width, + rect.height + ); + + let rect = ioctl::s_selection::<_, v4l2r::Rect>( + device, + ioctl::SelectionType::Output, + ioctl::SelectionTarget::Crop, + rect, + ioctl::SelectionFlags::empty(), + )?; + + if rect.left == 0 + && rect.top == 0 + && rect.width == visible_size.width + && rect.height == visible_size.height + { + log::trace!("Selection set successfully"); + } else { + log::warn!( + "Driver set selection to (left: {}, top: {}, width: {}, height: {})", + rect.left, + rect.top, + rect.width, + rect.height + ); + } + + Ok(()) + } + + /// Creates and sets up the backend instance using the given configuration + pub fn create( + device: Arc, + capture_buffers: CaptureBufferz, + fourcc: Fourcc, + coded_size: Resolution, + visible_size: Resolution, + capture_pixfmt: v4l2r::PixelFormat, + tunings: Tunings, + ) -> Result { + let mut capture_queue = Queue::get_capture_mplane_queue(device.clone()) + .map_err(InitializationError::CaptureQueueCreate)?; + + let mut output_queue = Queue::get_output_mplane_queue(device.clone()) + .map_err(InitializationError::OutputQueueCreate)?; + + // Coded buffer size multiplier. It's inteded to give head room for the encoder. + const CODED_SIZE_MUL: u32 = 2; + + // Default coded buffer size if bitrate control is not used. + const DEFAULT_CODED_SIZE: u32 = 1_500_000; + + let coded_buffer_size = tunings + .rate_control + .bitrate_target() + .map(|e| e as u32 * CODED_SIZE_MUL) + .unwrap_or(DEFAULT_CODED_SIZE); + + let capture_format = Format { + width: coded_size.width, + height: coded_size.height, + pixelformat: capture_pixfmt, + plane_fmt: vec![v4l2r::PlaneLayout { sizeimage: coded_buffer_size, bytesperline: 0 }], + }; + + let capture_format = capture_queue + .set_format(capture_format) + .map_err(InitializationError::SetFormatCapture)?; + + // TODO: Map single planar formats to mutli planar format if single planar is not + // supported. + let output_pixfmt: PixelFormat = fourcc.0.into(); + + let output_format = Format { + width: coded_size.width, + height: coded_size.height, + pixelformat: output_pixfmt, + // Let the driver pick + plane_fmt: vec![], + }; + + let output_format = + output_queue.set_format(output_format).map_err(InitializationError::SetFormatOutput)?; + + log::debug!("CAPTURE queue format = {capture_format:#?}"); + log::debug!("OUTPUT queue format = {output_format:#?}"); + + Self::apply_rate_control(&device, tunings.framerate, &tunings.rate_control)?; + Self::apply_tunings(&device, &tunings)?; + + Self::apply_ctrl(&device, "header mode", VideoHeaderMode::JoinedWith1stFrame)?; + + if visible_size.width > output_format.width || visible_size.height > output_format.height { + return Err(InitializationError::Unsupported(UnsupportedError::FrameUpscaling)); + } else if visible_size.width != output_format.width + || visible_size.height != output_format.height + { + log::info!("The frame visible size is not aligned to coded size, applying selection"); + if let Err(err) = Self::apply_selection(&device, visible_size) { + log::error!("Failed to set selection: {err:?}"); + } + } + + log::debug!("CAPTURE: Requesting buffers"); + let capture_queue = capture_queue + .request_buffers::<_>(16) + .map_err(InitializationError::RequestBufferOutput)?; + + log::debug!("OUTPUT: Requesting buffers"); + let output_queue = output_queue + .request_buffers::(16) + .map_err(InitializationError::RequestBufferOutput)?; + + log::debug!("CAPTURE: Invoking stream on"); + capture_queue.stream_on().map_err(InitializationError::StreamOnCapture)?; + + log::debug!("OUTPUT: Invoking stream on"); + output_queue.stream_on().map_err(InitializationError::StreamOnOutput)?; + + log::debug!("Sending start command to encoder"); + ioctl::encoder_cmd::<_, ()>(&device, &EncoderCommand::Start) + .map_err(InitializationError::EncoderStart)?; + + let mut poller = Poller::new(device.clone()).map_err(InitializationError::CreatePoller)?; + + poller + .enable_event(DeviceEvent::CaptureReady) + .map_err(InitializationError::CreatePoller)?; + + Ok(Self { + device, + output_queue, + capture_queue, + capture_buffers, + currently_processed: Default::default(), + current_tunings: tunings, + poller, + _phantom: Default::default(), + }) + } + + pub fn output_format>(&self) -> BackendResult { + Ok(self.output_queue.get_format()?) + } + + fn poll_device(&mut self) -> BackendResult<()> { + self.poller.poll(None)?; + + Ok(()) + } + + /// Attempts to queue all free CAPTURE buffer for filling with encoded bitstream + fn queue_capture(&mut self) -> BackendResult<()> { + while self.capture_queue.num_free_buffers() != 0 { + let buffer = self.capture_queue.try_get_free_buffer()?; + let buffer_index = buffer.index(); + + let queued = + self.capture_buffers.queue(buffer).map_err(BackendError::QueueBitstreamBuffer)?; + + if !queued { + log::warn!("CAPTURE: Capture buffer was queued. Will retry later"); + break; + } + + log::trace!("CAPTURE: Queued new buffer index={}", buffer_index); + } + + Ok(()) + } + + /// Tries to dequeue a CAPTURE buffer and transforms the buffer contents into [`BackendOutput`] + fn dequeue_capture(&mut self) -> BackendResult> { + if self.capture_queue.num_queued_buffers() == 0 { + // Don't dequeue if there is nothing to dequeue + log::warn!("Polled while no buffer was queued on CAPTURE queue"); + return Ok(None); + } + + let buffer = match self.capture_queue.try_dequeue() { + Ok(buffer) => buffer, + Err(ioctl::DqBufError::IoctlError( + err @ ioctl::DqBufIoctlError::NotReady | err @ ioctl::DqBufIoctlError::Eos, + )) => { + log::trace!("Dequeue result: {err:?}"); + return Ok(None); + } + Err(err) => return Err(err.into()), + }; + + let timestamp = Timestamp::from(buffer.data.timestamp()); + log::debug!( + "CAPTRUE: Dequeued buffer index={} timestamp={:?} is_last={} bytesused={}, flags={:?}", + buffer.data.index(), + timestamp, + buffer.data.is_last(), + *buffer.data.get_first_plane().bytesused, + buffer.data.flags(), + ); + + if *buffer.data.get_first_plane().bytesused == 0 { + // Don't warn about empty lasty buffer + if !buffer.data.is_last() { + log::warn!("CAPTURE: Dequeued empty buffer. Skipping it."); + } + return Ok(None); + } + + if !buffer.data.flags().intersects(BufferFlags::TIMESTAMP_COPY) { + log::error!("CAPTURE: Buffer does not have TIMESTAMP_COPY flag"); + return Err(BackendError::Unsupported(UnsupportedError::NoTimestampCopyFlag)); + } + + let Some((request_id, meta)) = self.currently_processed.remove(×tamp) else { + log::error!("CAPTURE: Failed to find buffer timestamp={timestamp:?}"); + return Err(BackendError::FailedToMapCapture(timestamp)); + }; + + let bitstream = + self.capture_buffers.export(buffer).map_err(BackendError::MapBitstreamBuffer)?; + + let output = + BackendOutput { request_id, buffer: CodedBitstreamBuffer::new(meta, bitstream) }; + + Ok(Some(output)) + } + + /// Dequeues all processed OUTPUT buffers and drops them + fn drain_output_queue(&mut self) -> BackendResult<()> { + // Don't dequeue if there is nothing to dequeue + while self.output_queue.num_queued_buffers() != 0 { + match self.output_queue.try_dequeue() { + Ok(buffer) => { + log::debug!( + "OUTPUT: Dequeued buffer index={} timestamp={:?}", + buffer.data.index(), + Timestamp::from(buffer.data.timestamp()) + ); + // Drop the finished buffer + drop(buffer); + } + Err(ioctl::DqBufError::IoctlError(ioctl::DqBufIoctlError::NotReady)) => break, + Err(ioctl::DqBufError::IoctlError(ioctl::DqBufIoctlError::Eos)) => {} + Err(err) => return Err(err.into()), + } + } + + Ok(()) + } + + /// Takes the [`BackendRequest`] and queues it to OUTPUT queue + fn handle_request(&mut self, request: BackendRequest) -> BackendResult<()> { + if self.current_tunings != request.tunings { + log::debug!("Changing tunings to {:#?}", request.tunings); + Self::apply_rate_control( + &self.device, + request.tunings.framerate, + &request.tunings.rate_control, + )?; + Self::apply_tunings(&self.device, &request.tunings)?; + self.current_tunings = request.tunings; + } + + let buffer = self.output_queue.try_get_free_buffer()?; + + let timestamp = Timestamp(request.meta.timestamp); + let buffer = buffer.set_timestamp(TimeVal::from(×tamp)); + + let index = buffer.index(); + + if request.meta.force_keyframe { + let mut force = SafeExtControl::::from_value(1); + ioctl::s_ext_ctrls(&self.device, ioctl::CtrlWhich::Current, &mut force).map_err( + |error| ControlError { which: "force keyframe", error: error.error.into() }, + )?; + } + + request.handle.queue(buffer).map_err(BackendError::QueueFrameHandleError)?; + + log::debug!("OUTPUT: Queued buffer index={} timestamp={:?}", index, timestamp); + + // TODO: Use RequestId for this? + self.currently_processed.insert(timestamp, (request.request_id, request.meta)); + + Ok(()) + } + + /// Performs the essential processing ie. queues and dequeues the buffers from CAPTURE and + /// OUTPUT queue. + fn handle_buffers(&mut self) -> BackendResult<()> { + self.queue_capture()?; + self.drain_output_queue()?; + + log::debug!( + "Queue status: OUTPUT(free: {}, queued: {}) CAPTURE(free: {}, queued: {})", + self.output_queue.num_free_buffers(), + self.output_queue.num_queued_buffers(), + self.capture_queue.num_free_buffers(), + self.capture_queue.num_queued_buffers(), + ); + + Ok(()) + } +} + +impl StatefulVideoEncoderBackend + for V4L2Backend +where + Handle: OutputBufferHandle, + CaptureBufferz: CaptureBuffers, + Self: EncoderCodec, +{ + fn consume_request( + &mut self, + request: &mut Option>, + ) -> StatefulBackendResult<()> { + self.handle_buffers()?; + + if self.output_queue.num_free_buffers() == 0 { + return Ok(()); + } + + let Some(request) = request.take() else { + log::error!("StatefulEncoder passed an empty request"); + return Err(StatefulBackendError::InvalidInternalState); + }; + + self.handle_request(request)?; + + Ok(()) + } + + fn sync(&mut self) -> StatefulBackendResult<()> { + self.poll_device()?; + Ok(()) + } + + fn poll(&mut self) -> StatefulBackendResult> { + Ok(self.dequeue_capture()?) + } + + fn drain(&mut self) -> StatefulBackendResult> { + if self.currently_processed.is_empty() { + log::info!("Skipping drain sequence, nothing to drain."); + return Ok(Vec::new()); + } + + log::debug!( + "Sending stop command to encoder. Currently processing count: {}", + self.currently_processed.len() + ); + + ioctl::encoder_cmd::<_, ()>(&self.device, &EncoderCommand::Stop(false)) + .map_err(BackendError::DrainCommand)?; + + let mut drained_output = Vec::new(); + while !self.currently_processed.is_empty() { + self.poll_device()?; + self.handle_buffers()?; + + if let Some(output) = self.dequeue_capture()? { + drained_output.push(output); + } + } + + // Dequeue is_last=true buffer + if let Some(output) = self.dequeue_capture()? { + drained_output.push(output); + } + + log::debug!("Sending start command to encoder"); + ioctl::encoder_cmd::<_, ()>(&self.device, &EncoderCommand::Start) + .map_err(BackendError::DrainCommand)?; + + log::debug!("Drain finished"); + Ok(drained_output) + } +} + +pub fn find_device_with_capture(pixfmt: v4l2r::PixelFormat) -> Option { + const MAX_DEVICE_NO: usize = 128; + for dev_no in 0..MAX_DEVICE_NO { + let device_path = PathBuf::from(format!("/dev/video{dev_no}")); + let Ok(device) = Device::open(&device_path, DeviceConfig::new()) else { + continue; + }; + + let device = Arc::new(device); + + let Ok(queue) = Queue::get_capture_mplane_queue(device) else { + continue; + }; + + for fmt in queue.format_iter() { + if fmt.pixelformat == pixfmt { + return Some(device_path); + } + } + } + + None +} + +pub fn v4l2_format_to_frame_layout(format: &v4l2r::Format) -> FrameLayout { + let mut layout = FrameLayout { + format: (Fourcc::from(format.pixelformat.to_u32()), 0), + size: Resolution { width: format.width, height: format.height }, + planes: format + .plane_fmt + .iter() + .map(|plane| crate::PlaneLayout { + buffer_index: 0, + offset: 0, + stride: plane.bytesperline as usize, + }) + .collect(), + }; + + // Patch FrameLayout + match &format.pixelformat.to_fourcc() { + b"NM12" if layout.planes.len() == 2 => { + layout.planes[1].buffer_index = 1; + } + b"NV12" if layout.planes.len() == 1 => {} + _ => panic!("Unknown format"), + }; + + layout +} + +#[cfg(test)] +pub(crate) mod tests { + use std::os::fd::AsFd; + use std::os::fd::BorrowedFd; + use std::os::fd::OwnedFd; + use std::path::Path; + use std::path::PathBuf; + + use anyhow::Context; + use v4l2r::device::queue::CaptureQueueable; + use v4l2r::device::DeviceConfig; + use v4l2r::memory::DmaBufSource; + + use super::*; + + use crate::backend::v4l2::encoder::CaptureBuffers; + use crate::encoder::simple_encode_loop; + use crate::encoder::stateful::StatefulEncoder; + use crate::encoder::tests::fill_test_frame_nm12; + use crate::encoder::tests::fill_test_frame_nv12; + use crate::encoder::tests::get_test_frame_t; + + /// A simple wrapper for a GBM device node. + pub struct GbmDevice(std::fs::File); + + impl AsFd for GbmDevice { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } + } + + impl drm::Device for GbmDevice {} + + /// Simple helper methods for opening a `Card`. + impl GbmDevice { + pub fn open>(path: P) -> std::io::Result { + std::fs::OpenOptions::new().read(true).write(true).open(path).map(GbmDevice) + } + } + + pub struct BoCaptureBuffer { + bo: gbm::BufferObject<()>, + fd: OwnedFd, + len: u64, + } + + impl AsRawFd for BoCaptureBuffer { + fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd { + self.fd.as_raw_fd() + } + } + + impl AsFd for BoCaptureBuffer { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } + } + + impl DmaBufSource for BoCaptureBuffer { + fn len(&self) -> u64 { + self.len + } + } + + impl std::fmt::Debug for BoCaptureBuffer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BoCaptureBuffer").finish() + } + } + + unsafe impl Sync for BoCaptureBuffer {} + + unsafe impl Send for BoCaptureBuffer {} + + // SAFETY: copied from gbm.h + pub const GBM_BO_USE_SW_READ_OFTEN: gbm::BufferObjectFlags = + unsafe { gbm::BufferObjectFlags::from_bits_truncate(1 << 9) }; + + // SAFETY: copied from gbm.h + pub const GBM_BO_USE_HW_VIDEO_ENCODER: gbm::BufferObjectFlags = + unsafe { gbm::BufferObjectFlags::from_bits_truncate(1 << 14) }; + + pub struct BoPoolAllocator { + gbm: Arc>, + } + + impl BoPoolAllocator { + pub fn new(gbm: Arc>) -> Self { + Self { gbm } + } + } + + impl CaptureBuffers for BoPoolAllocator { + type PlaneHandle = DmaBufHandle; + + fn queue( + &mut self, + buffer: QBuffer< + Capture, + Vec, + Vec, + &Queue>>, + >, + ) -> anyhow::Result { + let len = 2 * 1024 * 1024; + + log::trace!("Allocating new bo"); + let bo = self + .gbm + .create_buffer_object::<()>( + len as u32, + 1, + gbm::Format::R8, + GBM_BO_USE_HW_VIDEO_ENCODER | GBM_BO_USE_SW_READ_OFTEN, + ) + .context("gbm_bo_create")?; + + let fd = bo.fd_for_plane(0).unwrap(); + let handle = BoCaptureBuffer { bo, fd, len }; + + buffer + .queue_with_handles(vec![DmaBufHandle(handle)]) + .context("queue bo as dmabuf handle")?; + + Ok(true) + } + + fn export( + &self, + mut buffer: DqBuffer>, + ) -> anyhow::Result> { + let timestamp = Timestamp::from(buffer.data.timestamp()); + + let Some(mut handle) = buffer.take_handles() else { + log::error!("CAPTURE: Failed to map buffer timestamp={timestamp:?}"); + return Err(BackendError::FailedToMapCapture(timestamp).into()); + }; + + let Some(handle) = handle.pop() else { + log::error!("CAPTURE: Failed to map buffer timestamp={timestamp:?}"); + return Err(BackendError::FailedToMapCapture(timestamp).into()); + }; + + let bytesused = *buffer.data.get_first_plane().bytesused; + + let mut content = Vec::with_capacity(bytesused as usize); + + handle.0.bo.map(&self.gbm, 0, 0, bytesused, 1, |mapped| { + content.extend(mapped.buffer()); + })??; + + Ok(content) + } + } + + pub struct TestMmapFrame { + meta: FrameMetadata, + frame_count: u64, + } + + impl OutputBufferHandle for TestMmapFrame { + type PrimitiveBufferHandles = Vec; + + fn queue( + self, + buffer: OutputBuffer<'_, Self::PrimitiveBufferHandles>, + ) -> anyhow::Result<()> { + if self.meta.layout.format == (Fourcc::from(b"NM12"), 0) { + let mut y_plane = buffer.get_plane_mapping(0).unwrap(); + let mut uv_plane = buffer.get_plane_mapping(1).unwrap(); + + fill_test_frame_nm12( + self.meta.layout.size.width as usize, + self.meta.layout.size.height as usize, + [self.meta.layout.planes[0].stride, self.meta.layout.planes[1].stride], + get_test_frame_t(self.meta.timestamp, self.frame_count), + y_plane.as_mut(), + uv_plane.as_mut(), + ); + + buffer.queue(&[y_plane.len(), uv_plane.len()])?; + } else if self.meta.layout.format == (Fourcc::from(b"NV12"), 0) { + let mut plane = buffer.get_plane_mapping(0).unwrap(); + + let strides = + [self.meta.layout.planes[0].stride, self.meta.layout.planes[0].stride]; + let offsets = [ + self.meta.layout.planes[0].offset, + self.meta.layout.planes[0].stride * self.meta.layout.size.height as usize, + ]; + + fill_test_frame_nv12( + self.meta.layout.size.width as usize, + self.meta.layout.size.height as usize, + strides, + offsets, + get_test_frame_t(self.meta.timestamp, self.frame_count), + plane.as_mut(), + ); + + buffer.queue(&[plane.len()])?; + } else { + return Err(anyhow::anyhow!("unsupported format")); + } + + Ok(()) + } + } + + /// Helper struct. Procedurally generate NV12 or NM12 frames for test purposes. + pub struct TestMmapFrameGenerator { + counter: u64, + max_count: u64, + frame_layout: FrameLayout, + } + + impl TestMmapFrameGenerator { + pub fn new(max_count: u64, frame_layout: FrameLayout) -> Self { + Self { counter: 0, max_count, frame_layout } + } + } + + impl Iterator for TestMmapFrameGenerator { + type Item = (FrameMetadata, TestMmapFrame); + + fn next(&mut self) -> Option { + if self.counter > self.max_count { + return None; + } + + self.counter += 1; + + let meta = FrameMetadata { + timestamp: self.counter, + layout: self.frame_layout.clone(), + force_keyframe: false, + }; + + let handle = TestMmapFrame { meta: meta.clone(), frame_count: self.max_count }; + + Some((meta, handle)) + } + } + + pub fn perform_v4l2_encoder_mmap_test( + frame_count: u64, + mut encoder: StatefulEncoder< + TestMmapFrame, + V4L2Backend, + >, + coded_consumer: impl FnMut(CodedBitstreamBuffer), + ) where + V4L2Backend: EncoderCodec, + { + let format: v4l2r::Format = encoder.backend().output_format().unwrap(); + let layout = v4l2_format_to_frame_layout(&format); + let mut frame_producer = TestMmapFrameGenerator::new(frame_count, layout); + + simple_encode_loop(&mut encoder, &mut frame_producer, coded_consumer).expect("encode loop"); + } + + /// Helper struct. Procedurally generate NV12 or NM12 frames for test purposes. + pub struct TestDmabufFrameGenerator { + counter: u64, + max_count: u64, + coded_size: Resolution, + visible_size: Resolution, + gbm: Arc>, + } + + impl TestDmabufFrameGenerator { + pub fn new( + max_count: u64, + coded_size: Resolution, + visible_size: Resolution, + gbm: Arc>, + ) -> Self { + Self { counter: 0, max_count, coded_size, visible_size, gbm } + } + } + + impl Iterator for TestDmabufFrameGenerator { + type Item = (FrameMetadata, DmabufFrame); + + fn next(&mut self) -> Option { + if self.counter > self.max_count { + return None; + } + + self.counter += 1; + + let bo = self + .gbm + .create_buffer_object::<()>( + self.coded_size.width, + self.coded_size.height, + gbm::Format::Nv12, + GBM_BO_USE_HW_VIDEO_ENCODER, + ) + .expect("create bo"); + + let plane_count = bo.plane_count().unwrap() as i32; + let fourcc = bo.format().unwrap(); + + if plane_count > 2 { + // NOTE: NV12 should be at most 2 plane. + panic!("Unsupported plane count for bo"); + } + + let mut fds: Vec = Vec::new(); + let mut inodes: Vec = Vec::new(); + let mut planes = Vec::new(); + + for plane in 0..(bo.plane_count().unwrap() as i32) { + let fd = bo.fd_for_plane(plane).unwrap(); + let stat = fstat(fd.as_raw_fd()).unwrap(); + let offset = bo.offset(plane as _).unwrap() as usize; + let stride = bo.stride_for_plane(plane as _).unwrap() as usize; + let buffer_index; + + // Deduplicate fds + if let Some((index, _)) = + inodes.iter().enumerate().find(|(_, s)| **s == stat.st_ino) + { + buffer_index = index; + } else { + buffer_index = fds.len(); + fds.push(fd); + inodes.push(stat.st_ino); + } + + planes.push(crate::PlaneLayout { buffer_index, offset, stride }) + } + + let layout = FrameLayout { + format: (Fourcc::from(fourcc as u32), 0), + size: self.visible_size, + planes, + }; + dbg!(&layout); + + let meta = FrameMetadata { + timestamp: self.counter, + layout: layout.clone(), + force_keyframe: false, + }; + + let frame = DmabufFrame { fds, layout }; + + Some((meta, frame)) + } + } + + pub fn perform_v4l2_encoder_dmabuf_test( + coded_size: Resolution, + visible_size: Resolution, + frame_count: u64, + gbm: Arc>, + mut encoder: StatefulEncoder>, + coded_consumer: impl FnMut(CodedBitstreamBuffer), + ) where + V4L2Backend: EncoderCodec, + { + let format: v4l2r::Format = encoder.backend().output_format().unwrap(); + + let mut frame_producer = + TestDmabufFrameGenerator::new(frame_count, coded_size, visible_size, gbm).map( + |(meta, mut frame)| { + if frame.layout.format.0 == Fourcc::from(b"NV12") + && frame.layout.planes.len() == 2 + && format.pixelformat == PixelFormat::from_fourcc(b"NV12") + && format.plane_fmt.len() == 1 + { + // Remove last NV12 plane when GBM advertises 2 plaens and V4L2 expects a + // single frame. + frame.layout.planes.pop(); + } + + (meta, frame) + }, + ); + + simple_encode_loop(&mut encoder, &mut frame_producer, coded_consumer).expect("encode loop"); + } +} diff --git a/vendor/cros-codecs/src/backend/vaapi.rs b/vendor/cros-codecs/src/backend/vaapi.rs new file mode 100644 index 00000000..58320b3b --- /dev/null +++ b/vendor/cros-codecs/src/backend/vaapi.rs @@ -0,0 +1,270 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! VAAPI backend for both stateless decoders and encoders. + +use std::collections::HashSet; +use std::fmt::Debug; +use std::os::fd::AsRawFd; + +use anyhow::anyhow; +use libva::Display; +use libva::VAConfigAttrib; +use libva::VAConfigAttribType; + +use crate::utils::DmabufFrame; +use crate::utils::UserPtrFrame; +use crate::DecodedFormat; + +pub mod decoder; +pub mod encoder; +pub mod surface_pool; + +#[allow(dead_code)] +fn va_rt_format_to_string(va_rt_format: u32) -> String { + String::from(match va_rt_format { + libva::VA_RT_FORMAT_YUV420 => "YUV420", + libva::VA_RT_FORMAT_YUV422 => "YUV422", + libva::VA_RT_FORMAT_YUV444 => "YUV444", + libva::VA_RT_FORMAT_YUV420_10 => "YUV420_10", + libva::VA_RT_FORMAT_YUV420_12 => "YUV420_12", + libva::VA_RT_FORMAT_YUV422_10 => "YUV422_10", + libva::VA_RT_FORMAT_YUV422_12 => "YUV422_12", + libva::VA_RT_FORMAT_YUV444_10 => "YUV444_10", + libva::VA_RT_FORMAT_YUV444_12 => "YUV444_12", + other => return format!("unknown VA rt_format {}", other), + }) +} + +#[allow(dead_code)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +struct FormatMap { + pub rt_format: u32, + pub va_fourcc: u32, + pub decoded_format: DecodedFormat, +} + +/// Maps a given VA_RT_FORMAT to a compatible decoded format in an arbitrary +/// preferred order. +#[allow(dead_code)] +const FORMAT_MAP: [FormatMap; 10] = [ + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV420, + va_fourcc: libva::VA_FOURCC_NV12, + decoded_format: DecodedFormat::NV12, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV420, + va_fourcc: libva::VA_FOURCC_I420, + decoded_format: DecodedFormat::I420, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV422, + va_fourcc: libva::VA_FOURCC_422H, + decoded_format: DecodedFormat::I422, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV444, + va_fourcc: libva::VA_FOURCC_444P, + decoded_format: DecodedFormat::I444, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV420_10, + va_fourcc: libva::VA_FOURCC_P010, + decoded_format: DecodedFormat::I010, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV420_12, + va_fourcc: libva::VA_FOURCC_P012, + decoded_format: DecodedFormat::I012, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV422_10, + va_fourcc: libva::VA_FOURCC_Y210, + decoded_format: DecodedFormat::I210, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV422_12, + va_fourcc: libva::VA_FOURCC_Y212, + decoded_format: DecodedFormat::I212, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV444_10, + va_fourcc: libva::VA_FOURCC_Y410, + decoded_format: DecodedFormat::I410, + }, + FormatMap { + rt_format: libva::VA_RT_FORMAT_YUV444_12, + va_fourcc: libva::VA_FOURCC_Y412, + decoded_format: DecodedFormat::I412, + }, +]; + +/// Returns a set of supported decoded formats given `rt_format` +#[allow(dead_code)] +fn supported_formats_for_rt_format( + display: &Display, + rt_format: u32, + profile: i32, + entrypoint: u32, + image_formats: &[libva::VAImageFormat], +) -> anyhow::Result> { + let mut attrs = + vec![VAConfigAttrib { type_: VAConfigAttribType::VAConfigAttribRTFormat, value: 0 }]; + + display.get_config_attributes(profile, entrypoint, &mut attrs)?; + + // See whether this RT_FORMAT is supported by the given VAProfile and + // VAEntrypoint pair. + if attrs[0].value == libva::VA_ATTRIB_NOT_SUPPORTED || attrs[0].value & rt_format == 0 { + return Err(anyhow!( + "rt_format {:?} not supported for profile {:?} and entrypoint {:?}", + rt_format, + profile, + entrypoint + )); + } + + let mut supported_formats = HashSet::new(); + + for format in FORMAT_MAP { + if format.rt_format == rt_format { + supported_formats.insert(format); + } + } + + // Only retain those that the hardware can actually map into. + supported_formats + .retain(|&entry| image_formats.iter().any(|fmt| fmt.fourcc == entry.va_fourcc)); + + Ok(supported_formats) +} + +impl TryFrom<&libva::VAImageFormat> for DecodedFormat { + type Error = anyhow::Error; + + fn try_from(value: &libva::VAImageFormat) -> Result { + match value.fourcc { + libva::VA_FOURCC_I420 => Ok(DecodedFormat::I420), + libva::VA_FOURCC_NV12 => Ok(DecodedFormat::NV12), + libva::VA_FOURCC_P010 => Ok(DecodedFormat::I010), + libva::VA_FOURCC_P012 => Ok(DecodedFormat::I012), + libva::VA_FOURCC_Y210 => Ok(DecodedFormat::I210), + libva::VA_FOURCC_Y212 => Ok(DecodedFormat::I212), + libva::VA_FOURCC_Y410 => Ok(DecodedFormat::I410), + libva::VA_FOURCC_Y412 => Ok(DecodedFormat::I412), + _ => Err(anyhow!("Unsupported format")), + } + } +} + +impl libva::ExternalBufferDescriptor for UserPtrFrame { + const MEMORY_TYPE: libva::MemoryType = libva::MemoryType::UserPtr; + type DescriptorAttribute = libva::VASurfaceAttribExternalBuffers; + + fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute { + let pitches = self + .layout + .planes + .iter() + .map(|p| p.stride as u32) + .chain(std::iter::repeat(0)) + .take(4) + .collect::>() + .try_into() + .unwrap(); + let offsets = self + .layout + .planes + .iter() + .map(|p| p.offset as u32) + .chain(std::iter::repeat(0)) + .take(4) + .collect::>() + .try_into() + .unwrap(); + + libva::VASurfaceAttribExternalBuffers { + pixel_format: self.layout.format.0.into(), + width: self.layout.size.width, + height: self.layout.size.height, + data_size: self.mem_layout.size() as u32, + num_planes: self.layout.planes.len() as u32, + pitches, + offsets, + buffers: self.buffers.as_mut_ptr() as *mut _, + num_buffers: self.buffers.len() as u32, + flags: 0, + private_data: std::ptr::null_mut(), + } + } +} + +impl libva::ExternalBufferDescriptor for DmabufFrame { + const MEMORY_TYPE: libva::MemoryType = libva::MemoryType::DrmPrime2; + type DescriptorAttribute = libva::VADRMPRIMESurfaceDescriptor; + + fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute { + let objects = self + .fds + .iter() + .map(|fd| libva::VADRMPRIMESurfaceDescriptorObject { + fd: fd.as_raw_fd(), + size: nix::sys::stat::fstat(fd.as_raw_fd()) + .map(|stat| stat.st_size as u32) + // If we don't have the information about the plane fd size, fallback to 0. + // Libva seems to be *sometimes* "happy" with zero. + .unwrap_or(0), + // TODO should the descriptor be moved to individual objects? + drm_format_modifier: self.layout.format.1, + }) + .chain(std::iter::repeat(Default::default())) + .take(4) + .collect::>() + .try_into() + .unwrap(); + + let layers = [ + libva::VADRMPRIMESurfaceDescriptorLayer { + drm_format: self.layout.format.0.into(), + num_planes: self.layout.planes.len() as u32, + object_index: [0, 0, 0, 0], + offset: self + .layout + .planes + .iter() + .map(|p| p.offset as u32) + .chain(std::iter::repeat(0)) + .take(4) + .collect::>() + .try_into() + .unwrap(), + pitch: self + .layout + .planes + .iter() + .map(|p| p.stride as u32) + .chain(std::iter::repeat(0)) + .take(4) + .collect::>() + .try_into() + .unwrap(), + }, + Default::default(), + Default::default(), + Default::default(), + ]; + + libva::VADRMPRIMESurfaceDescriptor { + // TODO should we match and use VA_FOURCC_* here? + fourcc: self.layout.format.0.into(), + width: self.layout.size.width, + height: self.layout.size.height, + num_objects: 1, + objects, + num_layers: 1, + layers, + } + } +} diff --git a/vendor/cros-codecs/src/backend/vaapi/decoder.rs b/vendor/cros-codecs/src/backend/vaapi/decoder.rs new file mode 100644 index 00000000..fb291ce4 --- /dev/null +++ b/vendor/cros-codecs/src/backend/vaapi/decoder.rs @@ -0,0 +1,331 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::cell::RefCell; +use std::marker::PhantomData; +use std::rc::Rc; +use std::sync::Arc; + +use anyhow::anyhow; +use anyhow::Context as AnyhowContext; +use libva::{ + Buffer, Context, Display, Picture, PictureEnd, PictureNew, PictureSync, Surface, + SurfaceMemoryDescriptor, VaError, +}; + +use crate::decoder::stateless::StatelessBackendResult; +use crate::decoder::stateless::StatelessCodec; +use crate::decoder::stateless::StatelessDecoderBackend; +use crate::decoder::stateless::StatelessDecoderBackendPicture; +use crate::decoder::DecodedHandle as DecodedHandleTrait; +use crate::decoder::StreamInfo; +use crate::video_frame::VideoFrame; +use crate::DecodedFormat; +use crate::Rect; +use crate::Resolution; + +/// A decoded frame handle. +pub(crate) type DecodedHandle = Rc>>; + +/// Gets the VASurfaceID for the given `picture`. +pub(crate) fn va_surface_id( + handle: &Option>, +) -> libva::VASurfaceID { + match handle { + None => libva::VA_INVALID_SURFACE, + Some(handle) => handle.borrow().surface().id(), + } +} + +impl DecodedHandleTrait for DecodedHandle { + type Frame = V; + + fn video_frame(&self) -> Arc { + self.borrow().backing_frame.clone() + } + + fn coded_resolution(&self) -> Resolution { + self.borrow().surface().size().into() + } + + fn display_resolution(&self) -> Resolution { + self.borrow().display_resolution + } + + fn timestamp(&self) -> u64 { + self.borrow().timestamp() + } + + fn is_ready(&self) -> bool { + self.borrow().state.is_ready().unwrap_or(true) + } + + fn sync(&self) -> anyhow::Result<()> { + self.borrow_mut().sync().context("while syncing picture")?; + + Ok(()) + } +} + +/// A trait for providing the basic information needed to setup libva for decoding. +pub(crate) trait VaStreamInfo { + /// Returns the VA profile of the stream. + fn va_profile(&self) -> anyhow::Result; + /// Returns the RT format of the stream. + #[allow(dead_code)] + fn rt_format(&self) -> anyhow::Result; + /// Returns the minimum number of surfaces required to decode the stream. + fn min_num_surfaces(&self) -> usize; + /// Returns the coded size of the surfaces required to decode the stream. + fn coded_size(&self) -> Resolution; + /// Returns the visible rectangle within the coded size for the stream. + fn visible_rect(&self) -> Rect; +} + +/// Rendering state of a VA picture. +enum PictureState { + Ready(Picture>), + Pending(Picture>), + // Only set in sync when we take ownership of the VA picture. + Invalid, +} + +impl PictureState { + /// Make sure that all pending operations on the picture have completed. + fn sync(&mut self) -> Result<(), VaError> { + let res; + + (*self, res) = match std::mem::replace(self, PictureState::Invalid) { + state @ PictureState::Ready(_) => (state, Ok(())), + PictureState::Pending(picture) => match picture.sync() { + Ok(picture) => (PictureState::Ready(picture), Ok(())), + Err((e, picture)) => (PictureState::Pending(picture), Err(e)), + }, + PictureState::Invalid => unreachable!(), + }; + + res + } + + fn surface(&self) -> &Surface { + match self { + PictureState::Ready(picture) => picture.surface(), + PictureState::Pending(picture) => picture.surface(), + PictureState::Invalid => unreachable!(), + } + } + + fn timestamp(&self) -> u64 { + match self { + PictureState::Ready(picture) => picture.timestamp(), + PictureState::Pending(picture) => picture.timestamp(), + PictureState::Invalid => unreachable!(), + } + } + + fn is_ready(&self) -> Result { + match self { + PictureState::Ready(_) => Ok(true), + PictureState::Pending(picture) => picture + .surface() + .query_status() + .map(|s| s == libva::VASurfaceStatus::VASurfaceReady), + PictureState::Invalid => unreachable!(), + } + } + + fn new_from_same_surface(&self, timestamp: u64) -> Picture> { + match &self { + PictureState::Ready(picture) => Picture::new_from_same_surface(timestamp, picture), + PictureState::Pending(picture) => Picture::new_from_same_surface(timestamp, picture), + PictureState::Invalid => unreachable!(), + } + } +} + +/// VA-API backend handle. +/// +/// This includes the VA picture which can be pending rendering or complete, as well as useful +/// meta-information. +pub struct VaapiDecodedHandle { + backing_frame: Arc, + state: PictureState<::MemDescriptor>, + /// Actual resolution of the visible rectangle in the decoded buffer. + display_resolution: Resolution, +} + +impl VaapiDecodedHandle { + /// Creates a new pending handle on `surface_id`. + fn new(picture: VaapiPicture, display_resolution: Resolution) -> anyhow::Result { + let backing_frame = picture.backing_frame; + let picture = picture.picture.begin()?.render()?.end()?; + Ok(Self { + backing_frame: backing_frame, + state: PictureState::Pending(picture), + display_resolution: display_resolution, + }) + } + + fn sync(&mut self) -> Result<(), VaError> { + self.state.sync() + } + + /// Creates a new picture from the surface backing the current one. Useful for interlaced + /// decoding. TODO: Do we need this for other purposes? We don't intend to support interlaced. + pub(crate) fn new_picture_from_same_surface(&self, timestamp: u64) -> VaapiPicture { + VaapiPicture { + picture: self.state.new_from_same_surface(timestamp), + backing_frame: self.backing_frame.clone(), + } + } + + pub(crate) fn surface(&self) -> &Surface<::MemDescriptor> { + self.state.surface() + } + + /// Returns the timestamp of this handle. + fn timestamp(&self) -> u64 { + self.state.timestamp() + } +} + +pub struct VaapiBackend { + pub display: Rc, + pub context: Rc, + stream_info: StreamInfo, + // TODO: We should try to support context reuse + _supports_context_reuse: bool, + _phantom_data: PhantomData, +} + +impl VaapiBackend { + pub(crate) fn new(display: Rc, supports_context_reuse: bool) -> Self { + let init_stream_info = StreamInfo { + format: DecodedFormat::NV12, + coded_resolution: Resolution::from((16, 16)), + display_resolution: Resolution::from((16, 16)), + min_num_frames: 1, + }; + let config = display + .create_config( + vec![libva::VAConfigAttrib { + type_: libva::VAConfigAttribType::VAConfigAttribRTFormat, + value: libva::VA_RT_FORMAT_YUV420, + }], + libva::VAProfile::VAProfileH264Main, + libva::VAEntrypoint::VAEntrypointVLD, + ) + .expect("Could not create initial VAConfig!"); + let context = display + .create_context::<::MemDescriptor>( + &config, + init_stream_info.coded_resolution.width, + init_stream_info.coded_resolution.height, + None, + true, + ) + .expect("Could not create initial VAContext!"); + Self { + display: display, + context: context, + _supports_context_reuse: supports_context_reuse, + stream_info: init_stream_info, + _phantom_data: Default::default(), + } + } + + pub(crate) fn new_sequence( + &mut self, + stream_params: &StreamData, + ) -> StatelessBackendResult<()> + where + for<'a> &'a StreamData: VaStreamInfo, + { + self.stream_info.display_resolution = Resolution::from(stream_params.visible_rect()); + self.stream_info.coded_resolution = stream_params.coded_size().clone(); + self.stream_info.min_num_frames = stream_params.min_num_surfaces(); + + // TODO: Handle context re-use + // TODO: We should obtain RT_FORMAT from stream_info + let config = self + .display + .create_config( + vec![libva::VAConfigAttrib { + type_: libva::VAConfigAttribType::VAConfigAttribRTFormat, + value: libva::VA_RT_FORMAT_YUV420, + }], + stream_params.va_profile().map_err(|_| anyhow!("Could not get VAProfile!"))?, + libva::VAEntrypoint::VAEntrypointVLD, + ) + .map_err(|_| anyhow!("Could not create VAConfig!"))?; + let context = self + .display + .create_context::<::MemDescriptor>( + &config, + self.stream_info.coded_resolution.width, + self.stream_info.coded_resolution.height, + None, + true, + ) + .map_err(|_| anyhow!("Could not create VAContext!"))?; + self.context = context; + + Ok(()) + } + + pub(crate) fn process_picture( + &mut self, + picture: VaapiPicture, + ) -> StatelessBackendResult<::Handle> + where + Self: StatelessDecoderBackendPicture, + for<'a> &'a Codec::FormatInfo: VaStreamInfo, + { + Ok(Rc::new(RefCell::new(VaapiDecodedHandle::new( + picture, + self.stream_info.display_resolution.clone(), + )?))) + } +} + +/// Shortcut for pictures used for the VAAPI backend. +pub struct VaapiPicture { + picture: Picture>, + backing_frame: Arc, +} + +impl VaapiPicture { + pub fn new(timestamp: u64, context: Rc, backing_frame: V) -> Self { + let display = context.display(); + let surface = backing_frame + .to_native_handle(display) + .expect("Failed to export video frame to vaapi picture!") + .into(); + Self { + backing_frame: Arc::new(backing_frame), + picture: Picture::new(timestamp, context, surface), + } + } + + pub fn surface(&self) -> &Surface { + self.picture.surface() + } + + pub fn add_buffer(&mut self, buffer: Buffer) { + self.picture.add_buffer(buffer) + } +} + +impl StatelessDecoderBackend for VaapiBackend { + type Handle = DecodedHandle; + + fn stream_info(&self) -> Option<&StreamInfo> { + Some(&self.stream_info) + } + + fn reset_backend(&mut self) -> anyhow::Result<()> { + //TODO(bchoobineh): Implement VAAPI DRC + Ok(()) + } +} diff --git a/vendor/cros-codecs/src/backend/vaapi/encoder.rs b/vendor/cros-codecs/src/backend/vaapi/encoder.rs new file mode 100644 index 00000000..095c5b50 --- /dev/null +++ b/vendor/cros-codecs/src/backend/vaapi/encoder.rs @@ -0,0 +1,651 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::any::Any; +use std::marker::PhantomData; +use std::rc::Rc; + +use anyhow::anyhow; +use libva::Config; +use libva::Context; +use libva::Display; +use libva::EncCodedBuffer; +use libva::MappedCodedBuffer; +use libva::Picture; +use libva::PictureEnd; +use libva::Surface; +use libva::SurfaceMemoryDescriptor; +use libva::UsageHint; +use libva::VAEntrypoint::VAEntrypointEncSlice; +use libva::VAEntrypoint::VAEntrypointEncSliceLP; +use libva::VAProfile; +use libva::VASurfaceStatus; + +use crate::backend::vaapi::surface_pool::PooledVaSurface; +use crate::backend::vaapi::surface_pool::VaSurfacePool; +use crate::backend::vaapi::FORMAT_MAP; +use crate::decoder::FramePool; +use crate::encoder::stateless::BackendPromise; +use crate::encoder::stateless::StatelessBackendError; +use crate::encoder::stateless::StatelessBackendResult; +use crate::encoder::stateless::StatelessEncoderBackendImport; +use crate::encoder::FrameMetadata; +use crate::encoder::RateControl; +use crate::encoder::Tunings; +use crate::video_frame::VideoFrame; +use crate::Fourcc; +use crate::Resolution; + +/// The number of frames that encoder backend should initialize scratch pool with. +const INITIAL_SCRATCH_POOL_SIZE: usize = 16; +/// The maximum size of scratch pool size, after which the backend will refure to allocate more +/// scratch frames. +const MAX_SCRATCH_POOL_SIZE: usize = INITIAL_SCRATCH_POOL_SIZE * 4; + +impl From for StatelessBackendError { + fn from(value: libva::VaError) -> Self { + Self::Other(value.into()) + } +} + +pub(crate) fn tunings_to_libva_rc( + tunings: &Tunings, +) -> StatelessBackendResult { + let bits_per_second = tunings.rate_control.bitrate_target().unwrap_or(0); + let bits_per_second = u32::try_from(bits_per_second).map_err(|e| anyhow::anyhow!(e))?; + + // At the moment we don't support variable bitrate therefore target 100% + const TARGET_PERCENTAGE: u32 = 100; + + // Window size in ms that the RC should apply to + const WINDOW_SIZE: u32 = 1_500; + + // Clamp minium QP + let min_qp = tunings.min_quality.clamp(CLAMP_MIN_QP, CLAMP_MAX_QP); + + let basic_unit_size = 0; + + // Don't reset the rate controller + const RESET: u32 = 0; + + // Don't skip frames + const DISABLE_FRAME_SKIP: u32 = 1; + + // Allow bit stuffing + const DISABLE_BIT_STUFFING: u32 = 0; + + // Use default + const MB_RATE_CONTROL: u32 = 0; + + // SVC encoding is not supported for now + const TEMPORAL_ID: u32 = 0; + + // Don't ensure intraframe size + const CFS_I_FRAMES: u32 = 0; + + // We don't use hierarchical B frames currently + const ENABLE_PARALLEL_BRC: u32 = 0; + + // Disable dynamic scaling + const ENABLE_DYNAMIC_SCALING: u32 = 0; + + // Use default tolerance mode + const FRAME_TOLERANCE_MODE: u32 = 0; + + // ICQ mode is not used + const ICQ_QUALITY_FACTOR: u32 = 0; + + // Clamp maximum QP + let max_qp = tunings.max_quality.clamp(CLAMP_MIN_QP, CLAMP_MAX_QP); + + // Unsed + const QUALITY_FACTOR: u32 = 0; + + // No limits + const TARGET_FRAME_SIZE: u32 = 0; + + // If ConstantQuality is used then set to it's value, otherwise use middle + let initial_qp = match tunings.rate_control { + RateControl::ConstantQuality(qp) => qp.clamp(min_qp, max_qp), + _ => (min_qp + max_qp) / 2, + }; + + Ok(libva::EncMiscParameterRateControl::new( + bits_per_second, + TARGET_PERCENTAGE, + WINDOW_SIZE, + initial_qp, + min_qp, + basic_unit_size, + libva::RcFlags::new( + RESET, + DISABLE_FRAME_SKIP, + DISABLE_BIT_STUFFING, + MB_RATE_CONTROL, + TEMPORAL_ID, + CFS_I_FRAMES, + ENABLE_PARALLEL_BRC, + ENABLE_DYNAMIC_SCALING, + FRAME_TOLERANCE_MODE, + ), + ICQ_QUALITY_FACTOR, + max_qp, + QUALITY_FACTOR, + TARGET_FRAME_SIZE, + )) +} + +pub struct Reconstructed(PooledVaSurface<()>); + +impl Reconstructed { + pub(crate) fn surface(&self) -> &Surface<()> { + use std::borrow::Borrow; + Borrow::>::borrow(&self.0) + } + + pub(crate) fn surface_id(&self) -> u32 { + self.surface().id() + } +} + +pub struct VaapiBackend +where + M: SurfaceMemoryDescriptor, + H: std::borrow::Borrow> + 'static, +{ + /// VA config. + #[allow(dead_code)] + va_config: Config, + + /// VA context used for encoding. + context: Rc, + + _va_profile: VAProfile::Type, + scratch_pool: VaSurfacePool<()>, + _phantom: PhantomData<(M, H)>, +} + +impl VaapiBackend +where + M: SurfaceMemoryDescriptor, + H: std::borrow::Borrow>, +{ + pub fn new( + display: Rc, + va_profile: VAProfile::Type, + fourcc: Fourcc, + coded_size: Resolution, + bitrate_control: u32, + low_power: bool, + ) -> StatelessBackendResult { + let format_map = FORMAT_MAP + .iter() + .find(|&map| map.va_fourcc == fourcc.0) + .ok_or_else(|| StatelessBackendError::UnsupportedFormat)?; + + let rt_format = format_map.rt_format; + + let va_config = display.create_config( + vec![ + libva::VAConfigAttrib { + type_: libva::VAConfigAttribType::VAConfigAttribRTFormat, + value: rt_format, + }, + libva::VAConfigAttrib { + type_: libva::VAConfigAttribType::VAConfigAttribRateControl, + value: bitrate_control, + }, + ], + va_profile, + if low_power { VAEntrypointEncSliceLP } else { VAEntrypointEncSlice }, + )?; + + let context = display.create_context::( + &va_config, + coded_size.width, + coded_size.height, + None, + true, + )?; + + let mut scratch_pool = VaSurfacePool::new( + Rc::clone(&display), + rt_format, + Some(UsageHint::USAGE_HINT_ENCODER), + coded_size, + ); + + // TODO: Allow initial size to be changed + scratch_pool.add_frames(vec![(); INITIAL_SCRATCH_POOL_SIZE])?; + + Ok(Self { + va_config, + context, + scratch_pool, + _va_profile: va_profile, + _phantom: Default::default(), + }) + } + + pub(crate) fn context(&self) -> &Rc { + &self.context + } + + pub(crate) fn new_coded_buffer( + &self, + rate_control: &RateControl, + ) -> StatelessBackendResult { + // Coded buffer size multiplier. It's inteded to give head room for the encoder. + const CODED_SIZE_MUL: usize = 2; + + // Default coded buffer size if bitrate control is not used. + const DEFAULT_CODED_SIZE: usize = 1_500_000; + + let coded_size = rate_control + .bitrate_target() + .map(|e| e as usize * CODED_SIZE_MUL) + .unwrap_or(DEFAULT_CODED_SIZE); + + Ok(self.context().create_enc_coded(coded_size)?) + } + + // Creates an empty surface that will be filled with reconstructed picture during encoding + // which will be later used as frame reference + pub(crate) fn new_scratch_picture(&mut self) -> StatelessBackendResult { + if self.scratch_pool.num_free_frames() == 0 { + if self.scratch_pool.num_managed_frames() >= MAX_SCRATCH_POOL_SIZE { + log::error!("Scratch pool is exhausted and hit the size limit"); + return Err(StatelessBackendError::OutOfResources); + } + + log::debug!( + "Scratch pool empty, allocating one more surface. (previous pool size: {})", + self.scratch_pool.num_managed_frames() + ); + self.scratch_pool.add_frames(vec![()])?; + } + + let surface = + self.scratch_pool.get_surface().ok_or(StatelessBackendError::OutOfResources)?; + + Ok(Reconstructed(surface)) + } +} + +impl StatelessEncoderBackendImport for VaapiBackend +where + M: SurfaceMemoryDescriptor, + Handle: std::borrow::Borrow>, +{ + fn import_picture( + &mut self, + _metadata: &FrameMetadata, + handle: Handle, + ) -> StatelessBackendResult { + Ok(handle) + } +} + +impl StatelessEncoderBackendImport> + for VaapiBackend> +{ + fn import_picture( + &mut self, + _metadata: &FrameMetadata, + handle: V, + ) -> StatelessBackendResult> { + Ok(handle.to_native_handle(self.context.display()).map_err(|err| anyhow!(err))?.into()) + } +} + +/// Vaapi's implementation of [`crate::encoder::stateless::BackendPromise`] +pub struct CodedOutputPromise +where + M: SurfaceMemoryDescriptor, + P: std::borrow::Borrow>, +{ + /// Currently processed picture/surface. + handle: Picture, + + /// Hold reference frames/object from being dropped while `handle` is processed. + references: Vec>, + + // VaBuffer where the coded output will be present after processing + // is finished. + coded_buf: EncCodedBuffer, + + /// Container for the request output. Moved from + /// [`crate::encoder::stateless::StatelessVideoEncoderBackend`] request. The output will be + /// appended to it. + coded_output: Vec, + + _phantom: PhantomData, +} + +impl CodedOutputPromise +where + M: SurfaceMemoryDescriptor, + P: std::borrow::Borrow>, +{ + pub fn new( + handle: Picture, + references: Vec>, + coded_buf: EncCodedBuffer, + coded_output: Vec, + ) -> Self { + Self { handle, references, coded_buf, coded_output, _phantom: Default::default() } + } +} + +impl BackendPromise for CodedOutputPromise +where + M: SurfaceMemoryDescriptor, + H: std::borrow::Borrow>, +{ + type Output = Vec; + + fn sync(mut self) -> StatelessBackendResult { + if let Err((err, _)) = self.handle.sync() { + // TODO consider going back to PictureEnd + return Err(err.into()); + } + + // Drop all references as processing is finished + self.references.clear(); + + // Map coded buffer and collect bitstream + let coded = MappedCodedBuffer::new(&self.coded_buf)?; + let mut bitstream = self.coded_output; + for segment in coded.segments() { + // TODO: Handle flags? + // NOTE: on flags: 0-7 bits are average QP value + if segment.bit_offset > 0 { + log::warn!("unsupported bit_offset != 0 (yet)"); + } + bitstream.extend(segment.buf) + } + + Ok(bitstream) + } + + fn is_ready(&self) -> bool { + match self.handle.surface().query_status() { + Ok(status) => status == VASurfaceStatus::VASurfaceReady, + Err(_) => { + // An error occurred while processing or checking the status of the underlying + // processing, in both cases consider it is done. In either cases it will be + // returned with [`sync`]. + true + } + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + use std::borrow::Borrow; + + use libva::VA_FOURCC_NV12; + use libva::VA_FOURCC_P010; + + use super::*; + use crate::encoder::tests::fill_test_frame_nv12; + use crate::encoder::tests::fill_test_frame_p010; + use crate::encoder::tests::get_test_frame_t; + use crate::encoder::FrameMetadata; + use crate::FrameLayout; + + fn map_surface<'a, M: SurfaceMemoryDescriptor>( + display: &Rc, + surface: &'a Surface, + fourcc: u32, + ) -> libva::Image<'a> { + let image_fmts = display.query_image_formats().unwrap(); + let image_fmt = image_fmts.into_iter().find(|f| f.fourcc == fourcc).unwrap(); + + libva::Image::create_from(surface, image_fmt, surface.size(), surface.size()).unwrap() + } + + fn map_surface_nv12<'a, M: SurfaceMemoryDescriptor>( + display: &Rc, + surface: &'a Surface, + ) -> libva::Image<'a> { + map_surface(display, surface, VA_FOURCC_NV12) + } + + fn map_surface_p010<'a, M: SurfaceMemoryDescriptor>( + display: &Rc, + surface: &'a Surface, + ) -> libva::Image<'a> { + map_surface(display, surface, VA_FOURCC_P010) + } + + /// Uploads raw NV12 to Surface + pub fn upload_nv12_img( + display: &Rc, + surface: &Surface, + width: u32, + height: u32, + data: &[u8], + ) { + let mut image = map_surface_nv12(display, surface); + + let va_image = *image.image(); + let dest = image.as_mut(); + let width = width as usize; + let height = height as usize; + + let mut src: &[u8] = data; + let mut dst = &mut dest[va_image.offsets[0] as usize..]; + + // Copy luma + for _ in 0..height { + dst[..width].copy_from_slice(&src[..width]); + dst = &mut dst[va_image.pitches[0] as usize..]; + src = &src[width..]; + } + + // Advance to the offset of the chroma plane + let mut src = &data[width * height..]; + let mut dst = &mut dest[va_image.offsets[1] as usize..]; + + let height = height / 2; + + // Copy chroma + for _ in 0..height { + dst[..width].copy_from_slice(&src[..width]); + dst = &mut dst[va_image.pitches[1] as usize..]; + src = &src[width..]; + } + + surface.sync().unwrap(); + drop(image); + } + + /// Helper struct. [`Iterator`] to fetch frames from [`SurfacePool`]. + pub struct PooledFrameIterator { + counter: u64, + display: Rc, + pool: VaSurfacePool<()>, + frame_layout: FrameLayout, + } + + impl PooledFrameIterator { + pub fn new( + display: Rc, + pool: VaSurfacePool<()>, + frame_layout: FrameLayout, + ) -> Self { + Self { counter: 0, display, pool, frame_layout } + } + } + + impl Iterator for PooledFrameIterator { + type Item = (FrameMetadata, PooledVaSurface<()>); + + fn next(&mut self) -> Option { + let handle = self.pool.get_surface().unwrap(); + + let meta = FrameMetadata { + layout: self.frame_layout.clone(), + force_keyframe: false, + timestamp: self.counter, + }; + + self.counter += 1; + + Some((meta, handle)) + } + } + + /// Helper struct. Uses [`Iterator`] with raw chunks and uploads to pooled surface from + /// [`SurfacePool`] to produce frames. + pub struct NV12FrameProducer<'l, I> + where + I: Iterator, + { + raw_iterator: I, + pool_iter: PooledFrameIterator, + } + + impl<'l, I> NV12FrameProducer<'l, I> + where + I: Iterator, + { + #[allow(dead_code)] + pub fn new( + raw_iterator: I, + display: Rc, + pool: VaSurfacePool<()>, + frame_layout: FrameLayout, + ) -> Self { + Self { raw_iterator, pool_iter: PooledFrameIterator::new(display, pool, frame_layout) } + } + } + + impl<'l, I> Iterator for NV12FrameProducer<'l, I> + where + I: Iterator, + { + type Item = (FrameMetadata, PooledVaSurface<()>); + + fn next(&mut self) -> Option { + let raw = match self.raw_iterator.next() { + Some(raw) => raw, + None => return None, + }; + + let (meta, handle) = self.pool_iter.next().unwrap(); + + let width = meta.layout.size.width; + let height = meta.layout.size.height; + debug_assert_eq!((width * height + width * height / 2) as usize, raw.len()); + + upload_nv12_img(&self.pool_iter.display, handle.borrow(), width, height, raw); + + Some((meta, handle)) + } + } + + pub fn upload_test_frame_nv12( + display: &Rc, + surface: &Surface, + t: f32, + ) { + let mut image = map_surface_nv12(display, surface); + + let (width, height) = image.display_resolution(); + + let offsets = image.image().offsets; + let pitches = image.image().pitches; + + fill_test_frame_nv12( + width as usize, + height as usize, + [pitches[0] as usize, pitches[1] as usize], + [offsets[0] as usize, offsets[1] as usize], + t, + image.as_mut(), + ); + + drop(image); + surface.sync().unwrap(); + } + + pub fn upload_test_frame_p010( + display: &Rc, + surface: &Surface, + t: f32, + ) { + let mut image = map_surface_p010(display, surface); + + let (width, height) = image.display_resolution(); + + let offsets = image.image().offsets; + let pitches = image.image().pitches; + + fill_test_frame_p010( + width as usize, + height as usize, + [pitches[0] as usize, pitches[1] as usize], + [offsets[0] as usize, offsets[1] as usize], + t, + image.as_mut(), + ); + + drop(image); + surface.sync().unwrap(); + } + + /// Helper struct. Procedurally generate NV12 frames for test purposes. + pub struct TestFrameGenerator { + counter: u64, + max_count: u64, + pool_iter: PooledFrameIterator, + display: Rc, + fourcc: Fourcc, + } + + impl TestFrameGenerator { + pub fn new( + max_count: u64, + display: Rc, + pool: VaSurfacePool<()>, + frame_layout: FrameLayout, + ) -> Self { + Self { + counter: 0, + max_count, + fourcc: frame_layout.format.0, + pool_iter: PooledFrameIterator::new(display.clone(), pool, frame_layout), + display, + } + } + } + + impl Iterator for TestFrameGenerator { + type Item = (FrameMetadata, PooledVaSurface<()>); + + fn next(&mut self) -> Option { + if self.counter > self.max_count { + return None; + } + + self.counter += 1; + + let (meta, handle) = self.pool_iter.next().unwrap(); + + let surface: &Surface<()> = handle.borrow(); + + let t = get_test_frame_t(meta.timestamp, self.max_count); + match self.fourcc.0 { + VA_FOURCC_NV12 => upload_test_frame_nv12(&self.display, surface, t), + VA_FOURCC_P010 => upload_test_frame_p010(&self.display, surface, t), + _ => unreachable!(), + } + + Some((meta, handle)) + } + } +} diff --git a/vendor/cros-codecs/src/backend/vaapi/surface_pool.rs b/vendor/cros-codecs/src/backend/vaapi/surface_pool.rs new file mode 100644 index 00000000..a04ae912 --- /dev/null +++ b/vendor/cros-codecs/src/backend/vaapi/surface_pool.rs @@ -0,0 +1,225 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::borrow::Borrow; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::collections::VecDeque; +use std::rc::Rc; +use std::rc::Weak; + +use libva::Display; +use libva::Surface; +use libva::SurfaceMemoryDescriptor; +use libva::VASurfaceID; + +use crate::decoder::FramePool; +use crate::Resolution; + +/// A VA Surface obtained from a `[SurfacePool]`. +/// +/// The surface will automatically be returned to its pool upon dropping, provided the pool still +/// exists and the surface is still compatible with it. +pub struct PooledVaSurface { + surface: Option>, + pool: Weak>>, +} + +impl PooledVaSurface { + fn new(surface: Surface, pool: &Rc>>) -> Self { + Self { surface: Some(surface), pool: Rc::downgrade(pool) } + } + + /// Detach this surface from the pool. It will not be returned, and we can dispose of it + /// freely. + pub fn detach_from_pool(mut self) -> Surface { + // `unwrap` will never fail as `surface` is `Some` up to this point. + let surface = self.surface.take().unwrap(); + + if let Some(pool) = self.pool.upgrade() { + (*pool).borrow_mut().managed_surfaces.remove(&surface.id()); + } + + surface + } +} + +impl Borrow> for PooledVaSurface { + fn borrow(&self) -> &Surface { + // `unwrap` will never fail as `surface` is `Some` until the object is dropped. + self.surface.as_ref().unwrap() + } +} + +impl AsRef for PooledVaSurface { + fn as_ref(&self) -> &M { + >>::borrow(self).as_ref() + } +} + +impl Drop for PooledVaSurface { + fn drop(&mut self) { + // If the surface has not been detached... + if let Some(surface) = self.surface.take() { + // ... and the pool still exists... + if let Some(pool) = self.pool.upgrade() { + let mut pool_borrowed = (*pool).borrow_mut(); + // ... and the pool is still managing this surface, return it. + if pool_borrowed.managed_surfaces.contains_key(&surface.id()) { + pool_borrowed.surfaces.push_back(surface); + return; + } + } + + // The surface cannot be returned to the pool and can be gracefully dropped. + log::debug!("Dropping stale surface: {}, ({:?})", surface.id(), surface.size()) + } + } +} + +struct VaSurfacePoolInner { + display: Rc, + rt_format: u32, + usage_hint: Option, + coded_resolution: Resolution, + surfaces: VecDeque>, + /// All the surfaces managed by this pool, indexed by their surface ID. We keep their + /// resolution so we can remove them in case of a coded resolution change even if they + /// are currently borrowed. + managed_surfaces: BTreeMap, +} + +/// A surface pool to reduce the number of costly Surface allocations. +/// +/// The pool only houses Surfaces that fits the pool's coded resolution. +/// Stale surfaces are dropped when either the pool resolution changes, or when +/// stale surfaces are retrieved. +/// +/// This means that this pool is suitable for inter-frame DRC, as the stale +/// surfaces will gracefully be dropped, which is arguably better than the +/// alternative of having more than one pool active at a time. +pub struct VaSurfacePool { + inner: Rc>>, +} + +impl VaSurfacePool { + /// Add a surface to the pool. + /// + /// This can be an entirely new surface, or one that has been previously obtained using + /// `get_surface` and is returned. + /// + /// Returns an error (and the passed `surface` back) if the surface is not at least as + /// large as the current coded resolution of the pool. + #[allow(dead_code)] + fn add_surface(&mut self, surface: Surface) -> Result<(), Surface> { + let mut inner = (*self.inner).borrow_mut(); + + if Resolution::from(surface.size()).can_contain(inner.coded_resolution) { + inner.managed_surfaces.insert(surface.id(), surface.size().into()); + inner.surfaces.push_back(surface); + Ok(()) + } else { + Err(surface) + } + } + + /// Create a new pool. + /// + // # Arguments + /// + /// * `display` - the VA display to create the surfaces from. + /// * `rt_format` - the VA RT format to use for the surfaces. + /// * `usage_hint` - hint about how the surfaces from this pool will be used. + /// * `coded_resolution` - resolution of the surfaces. + pub fn new( + display: Rc, + rt_format: u32, + usage_hint: Option, + coded_resolution: Resolution, + ) -> Self { + Self { + inner: Rc::new(RefCell::new(VaSurfacePoolInner { + display, + rt_format, + usage_hint, + coded_resolution, + surfaces: VecDeque::new(), + managed_surfaces: Default::default(), + })), + } + } + + /// Gets a free surface from the pool. + pub fn get_surface(&mut self) -> Option> { + let mut inner = (*self.inner).borrow_mut(); + let surface = inner.surfaces.pop_front(); + + // Make sure the invariant holds when debugging. Can save costly + // debugging time during future refactors, if any. + debug_assert!({ + match surface.as_ref() { + Some(s) => Resolution::from(s.size()).can_contain(inner.coded_resolution), + None => true, + } + }); + + surface.map(|s| PooledVaSurface::new(s, &self.inner)) + } +} + +impl FramePool for VaSurfacePool { + type Descriptor = M; + + fn coded_resolution(&self) -> Resolution { + (*self.inner).borrow().coded_resolution + } + + fn set_coded_resolution(&mut self, resolution: Resolution) { + let mut inner = (*self.inner).borrow_mut(); + + inner.coded_resolution = resolution; + inner.managed_surfaces.retain(|_, res| res.can_contain(resolution)); + inner.surfaces.retain(|s| Resolution::from(s.size()).can_contain(resolution)); + } + + fn add_frames(&mut self, descriptors: Vec) -> Result<(), anyhow::Error> { + let mut inner = (*self.inner).borrow_mut(); + + let surfaces = inner + .display + .create_surfaces( + inner.rt_format, + // Let the hardware decide the best internal format - we will get the desired fourcc + // when creating the image. + None, + inner.coded_resolution.width, + inner.coded_resolution.height, + inner.usage_hint, + descriptors, + ) + .map_err(|e| anyhow::anyhow!(e))?; + + for surface in &surfaces { + inner.managed_surfaces.insert(surface.id(), surface.size().into()); + } + inner.surfaces.extend(surfaces); + + Ok(()) + } + + fn num_free_frames(&self) -> usize { + (*self.inner).borrow().surfaces.len() + } + + fn num_managed_frames(&self) -> usize { + (*self.inner).borrow().managed_surfaces.len() + } + + fn clear(&mut self) { + let mut pool = (*self.inner).borrow_mut(); + + pool.surfaces.clear(); + pool.managed_surfaces.clear(); + } +} diff --git a/vendor/cros-codecs/src/bitstream_utils.rs b/vendor/cros-codecs/src/bitstream_utils.rs new file mode 100644 index 00000000..d9ac0310 --- /dev/null +++ b/vendor/cros-codecs/src/bitstream_utils.rs @@ -0,0 +1,768 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::borrow::Cow; +use std::fmt; +use std::io::Cursor; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::io::Write; +use std::marker::PhantomData; + +use crate::codec::h264::parser::Nalu as H264Nalu; +use crate::codec::h265::parser::Nalu as H265Nalu; + +/// A bit reader for codec bitstreams. It properly handles emulation-prevention +/// bytes and stop bits for H264. +#[derive(Clone)] +pub(crate) struct BitReader<'a> { + /// A reference into the next unread byte in the stream. + data: Cursor<&'a [u8]>, + /// Contents of the current byte. First unread bit starting at position 8 - + /// num_remaining_bits_in_curr_bytes. + curr_byte: u8, + /// Number of bits remaining in `curr_byte` + num_remaining_bits_in_curr_byte: usize, + /// Used in emulation prevention byte detection. + prev_two_bytes: u16, + /// Number of emulation prevention bytes (i.e. 0x000003) we found. + num_epb: usize, + /// Whether or not we need emulation prevention logic. + needs_epb: bool, + /// How many bits have been read so far. + position: u64, +} + +#[derive(Debug)] +pub(crate) enum GetByteError { + OutOfBits, +} + +impl fmt::Display for GetByteError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "reader ran out of bits") + } +} + +#[derive(Debug)] +pub(crate) enum ReadBitsError { + TooManyBitsRequested(usize), + GetByte(GetByteError), + ConversionFailed, +} + +impl fmt::Display for ReadBitsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ReadBitsError::TooManyBitsRequested(bits) => { + write!(f, "more than 31 ({}) bits were requested", bits) + } + ReadBitsError::GetByte(_) => write!(f, "failed to advance the current byte"), + ReadBitsError::ConversionFailed => { + write!(f, "failed to convert read input to target type") + } + } + } +} + +impl From for ReadBitsError { + fn from(err: GetByteError) -> Self { + ReadBitsError::GetByte(err) + } +} + +impl<'a> BitReader<'a> { + pub fn new(data: &'a [u8], needs_epb: bool) -> Self { + Self { + data: Cursor::new(data), + curr_byte: Default::default(), + num_remaining_bits_in_curr_byte: Default::default(), + prev_two_bytes: 0xffff, + num_epb: Default::default(), + needs_epb: needs_epb, + position: 0, + } + } + + /// Read a single bit from the stream. + pub fn read_bit(&mut self) -> Result { + let bit = self.read_bits::(1)?; + match bit { + 1 => Ok(true), + 0 => Ok(false), + _ => panic!("Unexpected value {}", bit), + } + } + + /// Read up to 31 bits from the stream. Note that we don't want to read 32 + /// bits even though we're returning a u32 because that would break the + /// read_bits_signed() function. 31 bits should be overkill for compressed + /// header parsing anyway. + pub fn read_bits>(&mut self, num_bits: usize) -> Result { + if num_bits > 31 { + return Err(ReadBitsError::TooManyBitsRequested(num_bits).to_string()); + } + + let mut bits_left = num_bits; + let mut out = 0u32; + + while self.num_remaining_bits_in_curr_byte < bits_left { + out |= (self.curr_byte as u32) << (bits_left - self.num_remaining_bits_in_curr_byte); + bits_left -= self.num_remaining_bits_in_curr_byte; + self.move_to_next_byte().map_err(|err| err.to_string())?; + } + + out |= (self.curr_byte >> (self.num_remaining_bits_in_curr_byte - bits_left)) as u32; + out &= (1 << num_bits) - 1; + self.num_remaining_bits_in_curr_byte -= bits_left; + self.position += num_bits as u64; + + U::try_from(out).map_err(|_| ReadBitsError::ConversionFailed.to_string()) + } + + /// Reads a two's complement signed integer of length |num_bits|. + pub fn read_bits_signed>(&mut self, num_bits: usize) -> Result { + let mut out: i32 = self + .read_bits::(num_bits)? + .try_into() + .map_err(|_| ReadBitsError::ConversionFailed.to_string())?; + if out >> (num_bits - 1) != 0 { + out |= -1i32 ^ ((1 << num_bits) - 1); + } + + U::try_from(out).map_err(|_| ReadBitsError::ConversionFailed.to_string()) + } + + /// Reads an unsigned integer from the stream and checks if the stream is byte aligned. + pub fn read_bits_aligned>(&mut self, num_bits: usize) -> Result { + if self.num_remaining_bits_in_curr_byte % 8 != 0 { + return Err("Attempted unaligned read_le()".into()); + } + + Ok(self.read_bits(num_bits).map_err(|err| err.to_string())?) + } + + /// Skip `num_bits` bits from the stream. + pub fn skip_bits(&mut self, mut num_bits: usize) -> Result<(), String> { + while num_bits > 0 { + let n = std::cmp::min(num_bits, 31); + self.read_bits::(n)?; + num_bits -= n; + } + + Ok(()) + } + + /// Returns the amount of bits left in the stream + pub fn num_bits_left(&mut self) -> usize { + let cur_pos = self.data.position(); + // This should always be safe to unwrap. + let end_pos = self.data.seek(SeekFrom::End(0)).unwrap(); + let _ = self.data.seek(SeekFrom::Start(cur_pos)); + ((end_pos - cur_pos) as usize) * 8 + self.num_remaining_bits_in_curr_byte + } + + /// Returns the number of emulation-prevention bytes read so far. + pub fn num_epb(&self) -> usize { + self.num_epb + } + + /// Whether the stream still has RBSP data. Implements more_rbsp_data(). See + /// the spec for more details. + pub fn has_more_rsbp_data(&mut self) -> bool { + if self.num_remaining_bits_in_curr_byte == 0 && self.move_to_next_byte().is_err() { + // no more data at all in the rbsp + return false; + } + + // If the next bit is the stop bit, then we should only see unset bits + // until the end of the data. + if (self.curr_byte & ((1 << (self.num_remaining_bits_in_curr_byte - 1)) - 1)) != 0 { + return true; + } + + let mut buf = [0u8; 1]; + let orig_pos = self.data.position(); + while let Ok(_) = self.data.read_exact(&mut buf) { + if buf[0] != 0 { + self.data.set_position(orig_pos); + return true; + } + } + false + } + + /// Reads an Unsigned Exponential golomb coding number from the next bytes in the + /// bitstream. This may advance the state of position within the bitstream even if the + /// read operation is unsuccessful. See H264 Annex B specification 9.1 for details. + pub fn read_ue>(&mut self) -> Result { + let mut num_bits = 0; + + while self.read_bits::(1)? == 0 { + num_bits += 1; + if num_bits > 31 { + return Err("invalid stream".into()); + } + } + + let value = ((1u32 << num_bits) - 1) + .checked_add(self.read_bits::(num_bits)?) + .ok_or::("read number cannot fit in 32 bits".into())?; + + U::try_from(value).map_err(|_| "conversion error".into()) + } + + pub fn read_ue_bounded>(&mut self, min: u32, max: u32) -> Result { + let ue = self.read_ue()?; + if ue > max || ue < min { + Err(format!("Value out of bounds: expected {} - {}, got {}", min, max, ue)) + } else { + Ok(U::try_from(ue).map_err(|_| String::from("Conversion error"))?) + } + } + + pub fn read_ue_max>(&mut self, max: u32) -> Result { + self.read_ue_bounded(0, max) + } + + /// Reads a signed exponential golomb coding number. Instead of using two's + /// complement, this scheme maps even integers to positive numbers and odd + /// integers to negative numbers. The least significant bit indicates the + /// sign. See H264 Annex B specification 9.1.1 for details. + pub fn read_se>(&mut self) -> Result { + let ue = self.read_ue::()? as i32; + + if ue % 2 == 0 { + Ok(U::try_from(-(ue / 2)).map_err(|_| String::from("Conversion error"))?) + } else { + Ok(U::try_from(ue / 2 + 1).map_err(|_| String::from("Conversion error"))?) + } + } + + pub fn read_se_bounded>(&mut self, min: i32, max: i32) -> Result { + let se = self.read_se()?; + if se < min || se > max { + Err(format!("Value out of bounds, expected between {}-{}, got {}", min, max, se)) + } else { + Ok(U::try_from(se).map_err(|_| String::from("Conversion error"))?) + } + } + + /// Read little endian multi-byte integer. + pub fn read_le>(&mut self, num_bits: u8) -> Result { + let mut t = 0; + + for i in 0..num_bits { + let byte = self.read_bits_aligned::(8)?; + t += byte << (i * 8) + } + + Ok(U::try_from(t).map_err(|_| String::from("Conversion error"))?) + } + + /// Return the position of this bitstream in bits. + pub fn position(&self) -> u64 { + self.position + } + + fn get_byte(&mut self) -> Result { + let mut buf = [0u8; 1]; + self.data.read_exact(&mut buf).map_err(|_| GetByteError::OutOfBits)?; + Ok(buf[0]) + } + + fn move_to_next_byte(&mut self) -> Result<(), GetByteError> { + let mut byte = self.get_byte()?; + + if self.needs_epb { + if self.prev_two_bytes == 0 && byte == 0x03 { + // We found an epb + self.num_epb += 1; + // Read another byte + byte = self.get_byte()?; + // We need another 3 bytes before another epb can happen. + self.prev_two_bytes = 0xffff; + } + self.prev_two_bytes = (self.prev_two_bytes << 8) | u16::from(byte); + } + + self.num_remaining_bits_in_curr_byte = 8; + self.curr_byte = byte; + Ok(()) + } +} + +/// Iterator over IVF packets. +pub struct IvfIterator<'a> { + cursor: Cursor<&'a [u8]>, +} + +impl<'a> IvfIterator<'a> { + pub fn new(data: &'a [u8]) -> Self { + let mut cursor = Cursor::new(data); + + // Skip the IVH header entirely. + cursor.seek(std::io::SeekFrom::Start(32)).unwrap(); + + Self { cursor } + } +} + +impl<'a> Iterator for IvfIterator<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option { + // Make sure we have a header. + let mut len_buf = [0u8; 4]; + self.cursor.read_exact(&mut len_buf).ok()?; + let len = ((len_buf[3] as usize) << 24) + | ((len_buf[2] as usize) << 16) + | ((len_buf[1] as usize) << 8) + | (len_buf[0] as usize); + + // Skip PTS. + self.cursor.seek(std::io::SeekFrom::Current(8)).ok()?; + + let start = self.cursor.position() as usize; + let _ = self.cursor.seek(std::io::SeekFrom::Current(len as i64)).ok()?; + let end = self.cursor.position() as usize; + + Some(&self.cursor.get_ref()[start..end]) + } +} + +/// Helper struct for synthesizing IVF file header +pub struct IvfFileHeader { + pub magic: [u8; 4], + pub version: u16, + pub header_size: u16, + pub codec: [u8; 4], + pub width: u16, + pub height: u16, + pub framerate: u32, + pub timescale: u32, + pub frame_count: u32, + pub unused: u32, +} + +impl Default for IvfFileHeader { + fn default() -> Self { + Self { + magic: Self::MAGIC, + version: 0, + header_size: 32, + codec: Self::CODEC_VP9, + width: 320, + height: 240, + framerate: 1, + timescale: 1000, + frame_count: 1, + unused: Default::default(), + } + } +} + +impl IvfFileHeader { + pub const MAGIC: [u8; 4] = *b"DKIF"; + pub const CODEC_VP8: [u8; 4] = *b"VP80"; + pub const CODEC_VP9: [u8; 4] = *b"VP90"; + pub const CODEC_AV1: [u8; 4] = *b"AV01"; + + pub fn new(codec: [u8; 4], width: u16, height: u16, framerate: u32, frame_count: u32) -> Self { + let default = Self::default(); + + Self { + codec, + width, + height, + framerate: framerate * default.timescale, + frame_count, + ..default + } + } +} + +impl IvfFileHeader { + /// Writes header into writer + pub fn writo_into(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> { + writer.write_all(&self.magic)?; + writer.write_all(&self.version.to_le_bytes())?; + writer.write_all(&self.header_size.to_le_bytes())?; + writer.write_all(&self.codec)?; + writer.write_all(&self.width.to_le_bytes())?; + writer.write_all(&self.height.to_le_bytes())?; + writer.write_all(&self.framerate.to_le_bytes())?; + writer.write_all(&self.timescale.to_le_bytes())?; + writer.write_all(&self.frame_count.to_le_bytes())?; + writer.write_all(&self.unused.to_le_bytes())?; + + Ok(()) + } +} + +/// Helper struct for synthesizing IVF frame header +pub struct IvfFrameHeader { + pub frame_size: u32, + pub timestamp: u64, +} + +impl IvfFrameHeader { + /// Writes header into writer + pub fn writo_into(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> { + writer.write_all(&self.frame_size.to_le_bytes())?; + writer.write_all(&self.timestamp.to_le_bytes())?; + Ok(()) + } +} + +/// Iterator NALUs in a bitstream. +pub struct NalIterator<'a, Nalu>(Cursor<&'a [u8]>, PhantomData); + +impl<'a, Nalu> NalIterator<'a, Nalu> { + pub fn new(stream: &'a [u8]) -> Self { + Self(Cursor::new(stream), PhantomData) + } +} + +impl<'a> Iterator for NalIterator<'a, H264Nalu<'a>> { + type Item = Cow<'a, [u8]>; + + fn next(&mut self) -> Option { + H264Nalu::next(&mut self.0).map(|n| n.data).ok() + } +} + +impl<'a> Iterator for NalIterator<'a, H265Nalu<'a>> { + type Item = Cow<'a, [u8]>; + + fn next(&mut self) -> Option { + H265Nalu::next(&mut self.0).map(|n| n.data).ok() + } +} + +#[derive(Debug)] +pub enum BitWriterError { + InvalidBitCount, + Io(std::io::Error), +} + +impl fmt::Display for BitWriterError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + BitWriterError::InvalidBitCount => write!(f, "invalid bit count"), + BitWriterError::Io(x) => write!(f, "{}", x.to_string()), + } + } +} + +impl From for BitWriterError { + fn from(err: std::io::Error) -> Self { + BitWriterError::Io(err) + } +} + +pub type BitWriterResult = std::result::Result; + +pub struct BitWriter { + out: W, + nth_bit: u8, + curr_byte: u8, +} + +impl BitWriter { + pub fn new(writer: W) -> Self { + Self { out: writer, curr_byte: 0, nth_bit: 0 } + } + + /// Writes fixed bit size integer (up to 32 bit) + pub fn write_f>(&mut self, bits: usize, value: T) -> BitWriterResult { + let value = value.into(); + + if bits > 32 { + return Err(BitWriterError::InvalidBitCount); + } + + let mut written = 0; + for bit in (0..bits).rev() { + let bit = (1 << bit) as u32; + + self.write_bit((value & bit) == bit)?; + written += 1; + } + + Ok(written) + } + + /// Takes a single bit that will be outputed to [`std::io::Write`] + pub fn write_bit(&mut self, bit: bool) -> BitWriterResult<()> { + self.curr_byte |= (bit as u8) << (7u8 - self.nth_bit); + self.nth_bit += 1; + + if self.nth_bit == 8 { + self.out.write_all(&[self.curr_byte])?; + self.nth_bit = 0; + self.curr_byte = 0; + } + + Ok(()) + } + + /// Immediately outputs any cached bits to [`std::io::Write`] + pub fn flush(&mut self) -> BitWriterResult<()> { + if self.nth_bit != 0 { + self.out.write_all(&[self.curr_byte])?; + self.nth_bit = 0; + self.curr_byte = 0; + } + + self.out.flush()?; + Ok(()) + } + + /// Returns `true` if ['Self`] hold data that wasn't written to [`std::io::Write`] + pub fn has_data_pending(&self) -> bool { + self.nth_bit != 0 + } + + pub(crate) fn inner(&self) -> &W { + &self.out + } + + pub(crate) fn inner_mut(&mut self) -> &mut W { + &mut self.out + } +} + +impl Drop for BitWriter { + fn drop(&mut self) { + if let Err(e) = self.flush() { + log::error!("Unable to flush bits {e:?}"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ivf_file_header() { + let mut hdr = IvfFileHeader { + version: 0, + codec: IvfFileHeader::CODEC_VP9, + width: 256, + height: 256, + framerate: 30_000, + timescale: 1_000, + frame_count: 1, + + ..Default::default() + }; + + let mut buf = Vec::new(); + hdr.writo_into(&mut buf).unwrap(); + + const EXPECTED: [u8; 32] = [ + 0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x39, 0x30, 0x00, 0x01, + 0x00, 0x01, 0x30, 0x75, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + assert_eq!(&buf, &EXPECTED); + + hdr.width = 1920; + hdr.height = 800; + hdr.framerate = 24; + hdr.timescale = 1; + hdr.frame_count = 100; + + buf.clear(); + hdr.writo_into(&mut buf).unwrap(); + + const EXPECTED2: [u8; 32] = [ + 0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x39, 0x30, 0x80, 0x07, + 0x20, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + assert_eq!(&buf, &EXPECTED2); + } + + #[test] + fn test_ivf_frame_header() { + let mut hdr = IvfFrameHeader { frame_size: 199249, timestamp: 0 }; + + let mut buf = Vec::new(); + hdr.writo_into(&mut buf).unwrap(); + + const EXPECTED: [u8; 12] = + [0x51, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + + assert_eq!(&buf, &EXPECTED); + + hdr.timestamp = 1; + hdr.frame_size = 52; + + buf.clear(); + hdr.writo_into(&mut buf).unwrap(); + + const EXPECTED2: [u8; 12] = + [0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + + assert_eq!(&buf, &EXPECTED2); + } + + #[test] + fn test_bitwriter_f1() { + let mut buf = Vec::::new(); + { + let mut writer = BitWriter::new(&mut buf); + writer.write_f(1, true).unwrap(); + writer.write_f(1, false).unwrap(); + writer.write_f(1, false).unwrap(); + writer.write_f(1, false).unwrap(); + writer.write_f(1, true).unwrap(); + writer.write_f(1, true).unwrap(); + writer.write_f(1, true).unwrap(); + writer.write_f(1, true).unwrap(); + } + assert_eq!(buf, vec![0b10001111u8]); + } + + #[test] + fn test_bitwriter_f3() { + let mut buf = Vec::::new(); + { + let mut writer = BitWriter::new(&mut buf); + writer.write_f(3, 0b100u8).unwrap(); + writer.write_f(3, 0b101u8).unwrap(); + writer.write_f(3, 0b011u8).unwrap(); + } + assert_eq!(buf, vec![0b10010101u8, 0b10000000u8]); + } + + #[test] + fn test_bitwriter_f4() { + let mut buf = Vec::::new(); + { + let mut writer = BitWriter::new(&mut buf); + writer.write_f(4, 0b1000u8).unwrap(); + writer.write_f(4, 0b1011u8).unwrap(); + } + assert_eq!(buf, vec![0b10001011u8]); + } + + // These tests are adapted from the chromium tests at media/video/h264_bit_reader_unitttest.cc + + #[test] + fn read_stream_without_escape_and_trailing_zero_bytes() { + const RBSP: [u8; 6] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xa0]; + + let mut reader = BitReader::new(&RBSP, true); + assert_eq!(reader.read_bits::(1).unwrap(), 0); + assert_eq!(reader.num_bits_left(), 47); + assert!(reader.has_more_rsbp_data()); + + assert_eq!(reader.read_bits::(8).unwrap(), 0x02); + assert_eq!(reader.num_bits_left(), 39); + assert!(reader.has_more_rsbp_data()); + + assert_eq!(reader.read_bits::(31).unwrap(), 0x23456789); + assert_eq!(reader.num_bits_left(), 8); + assert!(reader.has_more_rsbp_data()); + + assert_eq!(reader.read_bits::(1).unwrap(), 1); + assert_eq!(reader.num_bits_left(), 7); + assert!(reader.has_more_rsbp_data()); + + assert_eq!(reader.read_bits::(1).unwrap(), 0); + assert_eq!(reader.num_bits_left(), 6); + assert!(!reader.has_more_rsbp_data()); + } + + #[test] + fn single_byte_stream() { + const RBSP: [u8; 1] = [0x18]; + + let mut reader = BitReader::new(&RBSP, true); + assert_eq!(reader.num_bits_left(), 8); + assert!(reader.has_more_rsbp_data()); + assert_eq!(reader.read_bits::(4).unwrap(), 1); + assert!(!reader.has_more_rsbp_data()); + } + + #[test] + fn stop_bit_occupy_full_byte() { + const RBSP: [u8; 2] = [0xab, 0x80]; + + let mut reader = BitReader::new(&RBSP, true); + assert_eq!(reader.num_bits_left(), 16); + assert!(reader.has_more_rsbp_data()); + + assert_eq!(reader.read_bits::(8).unwrap(), 0xab); + assert_eq!(reader.num_bits_left(), 8); + + assert!(!reader.has_more_rsbp_data()); + } + + // Check that read_ue behaves properly with input at the limits. + #[test] + fn read_ue() { + // Regular value. + let mut reader = BitReader::new(&[0b0001_1010], true); + assert_eq!(reader.read_ue::().unwrap(), 12); + assert_eq!(reader.data.position(), 1); + assert_eq!(reader.num_remaining_bits_in_curr_byte, 1); + + // 0 value. + let mut reader = BitReader::new(&[0b1000_0000], true); + assert_eq!(reader.read_ue::().unwrap(), 0); + assert_eq!(reader.data.position(), 1); + assert_eq!(reader.num_remaining_bits_in_curr_byte, 7); + + // No prefix stop bit. + let mut reader = BitReader::new(&[0b0000_0000], true); + reader.read_ue::().unwrap_err(); + + // u32 max value: 31 0-bits, 1 bit marker, 31 bits 1-bits. + let mut reader = BitReader::new( + &[ + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + ], + true, + ); + assert_eq!(reader.read_ue::().unwrap(), 0xffff_fffe); + assert_eq!(reader.data.position(), 8); + assert_eq!(reader.num_remaining_bits_in_curr_byte, 1); + } + + // Check that emulation prevention is being handled correctly. + #[test] + fn skip_epb_when_enabled() { + let mut reader = BitReader::new(&[0x00, 0x00, 0x03, 0x01], false); + assert_eq!(reader.read_bits::(8).unwrap(), 0x00); + assert_eq!(reader.read_bits::(8).unwrap(), 0x00); + assert_eq!(reader.read_bits::(8).unwrap(), 0x03); + assert_eq!(reader.read_bits::(8).unwrap(), 0x01); + + let mut reader = BitReader::new(&[0x00, 0x00, 0x03, 0x01], true); + assert_eq!(reader.read_bits::(8).unwrap(), 0x00); + assert_eq!(reader.read_bits::(8).unwrap(), 0x00); + assert_eq!(reader.read_bits::(8).unwrap(), 0x01); + } + + #[test] + fn read_signed_bits() { + let mut reader = BitReader::new(&[0b1111_0000], false); + assert_eq!(reader.read_bits_signed::(4).unwrap(), -1); + } +} diff --git a/vendor/cros-codecs/src/c2_wrapper.rs b/vendor/cros-codecs/src/c2_wrapper.rs new file mode 100644 index 00000000..97b33092 --- /dev/null +++ b/vendor/cros-codecs/src/c2_wrapper.rs @@ -0,0 +1,381 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use nix::errno::Errno; +use nix::sys::eventfd::EfdFlags; +use nix::sys::eventfd::EventFd; + +use thiserror::Error; + +use std::collections::VecDeque; +use std::marker::PhantomData; +use std::sync::atomic::AtomicU32; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; +use std::thread::JoinHandle; +use std::vec::Vec; + +use crate::decoder::StreamInfo; +use crate::video_frame::VideoFrame; +use crate::Fourcc; + +pub mod c2_decoder; +pub mod c2_encoder; +#[cfg(feature = "v4l2")] +pub mod c2_v4l2_decoder; +#[cfg(feature = "v4l2")] +pub mod c2_v4l2_encoder; +#[cfg(feature = "vaapi")] +pub mod c2_vaapi_decoder; +#[cfg(feature = "vaapi")] +pub mod c2_vaapi_encoder; + +#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)] +pub enum DrainMode { + // Not draining + #[default] + NoDrain = -1, + // Drain the C2 component and signal an EOS. Currently we also change the state to stop. + EOSDrain = 0, + // Drain the C2 component, but keep accepting new jobs in the queue immediately after. + NoEOSDrain = 1, +} + +#[derive(Debug)] +pub struct C2DecodeJob { + // Compressed input data + // TODO: Use VideoFrame for input too + pub input: Vec, + // Decompressed output frame. Note that this needs to be reference counted because we may still + // use this frame as a reference frame even while we're displaying it. + pub output: Option>, + pub drain: DrainMode, + // TODO: Add output delay and color aspect support as needed. +} + +impl Job for C2DecodeJob +where + V: VideoFrame, +{ + type Frame = V; + + fn set_drain(&mut self, drain: DrainMode) { + self.drain = drain; + } + + fn get_drain(&self) -> DrainMode { + self.drain + } +} + +impl Default for C2DecodeJob { + fn default() -> Self { + Self { input: vec![], output: None, drain: DrainMode::NoDrain } + } +} + +pub trait Job: Send + 'static { + type Frame: VideoFrame; + + fn set_drain(&mut self, drain: DrainMode) -> (); + fn get_drain(&self) -> DrainMode; +} + +#[derive(Debug)] +pub struct C2EncodeJob { + pub input: Option, + // TODO: Use VideoFrame for output too + pub output: Vec, + // In microseconds. + pub timestamp: u64, + // TODO: only support CBR right now, follow up with VBR support. + pub bitrate: u64, + // Framerate is actually negotiated, so the encoder can change this value + // based on the timestamps of the frames it receives. + pub framerate: Arc, + pub drain: DrainMode, +} + +impl Default for C2EncodeJob { + fn default() -> Self { + Self { + input: None, + output: vec![], + timestamp: 0, + bitrate: 0, + framerate: Arc::new(AtomicU32::new(0)), + drain: DrainMode::NoDrain, + } + } +} + +impl Job for C2EncodeJob +where + V: VideoFrame, +{ + type Frame = V; + + fn set_drain(&mut self, drain: DrainMode) { + self.drain = drain; + } + + fn get_drain(&self) -> DrainMode { + self.drain + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum C2State { + C2Running, + C2Stopped, + // Note that on state C2Error, stop() must be called before we can start() + // again. + C2Error, +} + +// This is not a very "Rust-y" way of doing error handling, but it will +// hopefully make the FFI easier to write. Numerical values taken from +// frameworks/av/media/codec2/core/include/C2.h +// TODO: Improve error handling by adding more statuses. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum C2Status { + C2Ok = 0, + C2BadState = 1, // EPERM + C2BadValue = 22, // EINVAL +} + +// J should be either C2DecodeJob or C2EncodeJob. +pub trait C2Worker +where + J: Send + Job + 'static, +{ + type Options: Clone + Send + 'static; + + fn new( + input_fourcc: Fourcc, + output_fourcc: Fourcc, + awaiting_job_event: Arc, + error_cb: Arc>, + work_done_cb: Arc>, + work_queue: Arc>>, + state: Arc>, + framepool_hint_cb: Arc>, + alloc_cb: Arc Option<::Frame> + Send + 'static>>, + options: Self::Options, + ) -> Result + where + Self: Sized; + + fn process_loop(&mut self); +} + +#[derive(Debug, Error)] +pub enum C2WrapperError { + #[error("failed to create EventFd for awaiting job event: {0}")] + AwaitingJobEventFd(Errno), +} + +// Note that we do not guarantee thread safety in C2CrosCodecsWrapper. +pub struct C2Wrapper +where + J: Send + Default + Job + 'static, + W: C2Worker, +{ + awaiting_job_event: Arc, + input_fourcc: Fourcc, + output_fourcc: Fourcc, + error_cb: Arc>, + work_done_cb: Arc>, + work_queue: Arc>>, + state: Arc>, + framepool_hint_cb: Arc>, + alloc_cb: Arc Option<::Frame> + Send + 'static>>, + options: >::Options, + worker_thread: Option>, + // The instance of V actually lives in the thread creation closure, not + // this struct. + _phantom: PhantomData, +} + +impl C2Wrapper +where + J: Send + Default + Job + 'static, + W: C2Worker, +{ + pub fn new( + input_fourcc: Fourcc, + output_fourcc: Fourcc, + error_cb: impl FnMut(C2Status) + Send + 'static, + work_done_cb: impl FnMut(J) + Send + 'static, + framepool_hint_cb: impl FnMut(StreamInfo) + Send + 'static, + alloc_cb: impl FnMut() -> Option<::Frame> + Send + 'static, + options: >::Options, + ) -> Self { + Self { + awaiting_job_event: Arc::new( + EventFd::from_flags(EfdFlags::EFD_SEMAPHORE) + .map_err(C2WrapperError::AwaitingJobEventFd) + .unwrap(), + ), + input_fourcc: input_fourcc, + output_fourcc: output_fourcc, + error_cb: Arc::new(Mutex::new(error_cb)), + work_done_cb: Arc::new(Mutex::new(work_done_cb)), + work_queue: Arc::new(Mutex::new(VecDeque::new())), + state: Arc::new(Mutex::new(C2State::C2Stopped)), + framepool_hint_cb: Arc::new(Mutex::new(framepool_hint_cb)), + alloc_cb: Arc::new(Mutex::new(alloc_cb)), + options: options, + worker_thread: None, + _phantom: Default::default(), + } + } + + // This isn't part of C2, but it's convenient to check if our worker thread + // is still running. + pub fn is_alive(&self) -> bool { + match &self.worker_thread { + Some(worker_thread) => !worker_thread.is_finished(), + None => false, + } + } + + // Start the decoder/encoder. + // State will be C2Running after this call. + pub fn start(&mut self) -> C2Status { + { + let mut state = self.state.lock().unwrap(); + if *state != C2State::C2Stopped { + (*self.error_cb.lock().unwrap())(C2Status::C2BadState); + return C2Status::C2BadState; + } + *state = C2State::C2Running; + } + + let input_fourcc = self.input_fourcc.clone(); + let output_fourcc = self.output_fourcc.clone(); + let error_cb = self.error_cb.clone(); + let work_done_cb = self.work_done_cb.clone(); + let work_queue = self.work_queue.clone(); + let state = self.state.clone(); + let options = self.options.clone(); + let awaiting_job_event = self.awaiting_job_event.clone(); + let framepool_hint_cb = self.framepool_hint_cb.clone(); + let alloc_cb = self.alloc_cb.clone(); + self.worker_thread = Some(thread::spawn(move || { + let worker = W::new( + input_fourcc, + output_fourcc, + awaiting_job_event, + error_cb.clone(), + work_done_cb, + work_queue, + state.clone(), + framepool_hint_cb, + alloc_cb, + options, + ); + match worker { + Ok(mut worker) => worker.process_loop(), + Err(msg) => { + log::debug!("Error instantiating C2Worker {}", msg); + *state.lock().unwrap() = C2State::C2Error; + (*error_cb.lock().unwrap())(C2Status::C2BadValue); + } + }; + })); + + C2Status::C2Ok + } + + // Stop the decoder/encoder and abandon in-flight work. + // C2's reset() function is equivalent for our purposes. + // Note that in event of error, stop() must be called before we can start() + // again. This is to ensure we clear out the work queue. + // State will be C2Stopped after this call. + pub fn stop(&mut self) -> C2Status { + *self.state.lock().unwrap() = C2State::C2Stopped; + let mut worker_thread: Option> = None; + std::mem::swap(&mut worker_thread, &mut self.worker_thread); + self.worker_thread = match worker_thread { + Some(worker_thread) => { + let _ = worker_thread.join(); + None + } + None => None, + }; + + self.work_queue.lock().unwrap().drain(..); + + self.awaiting_job_event.write(1).unwrap(); + + C2Status::C2Ok + } + + // Add work to the work queue. + // State must be C2Running or this function is invalid. + // State will remain C2Running. + pub fn queue(&mut self, work_items: Vec) -> C2Status { + if *self.state.lock().unwrap() != C2State::C2Running { + (*self.error_cb.lock().unwrap())(C2Status::C2BadState); + return C2Status::C2BadState; + } + + self.work_queue.lock().unwrap().extend(work_items.into_iter()); + + self.awaiting_job_event.write(1).unwrap(); + + C2Status::C2Ok + } + + // Flush work from the queue and return it as |flushed_work|. + // State will not change after this call. + // TODO: Support different flush modes. + pub fn flush(&mut self, flushed_work: &mut Vec) -> C2Status { + if *self.state.lock().unwrap() != C2State::C2Running { + (*self.error_cb.lock().unwrap())(C2Status::C2BadState); + return C2Status::C2BadState; + } + + let mut tmp = self.work_queue.lock().unwrap().drain(..).collect::>(); + flushed_work.append(&mut tmp); + + C2Status::C2Ok + } + + // Signal to the decoder/encoder that it does not need to wait for + // additional work to begin processing. This is an unusual name for this + // function, but it is the convention that C2 uses. + // State must be C2Running or this function is invalid. + // State will remain C2Running until the last frames drain, at which point + // the state will change to C2Stopped. + // TODO: Support different drain modes. + pub fn drain(&mut self, mode: DrainMode) -> C2Status { + if *self.state.lock().unwrap() != C2State::C2Running { + (*self.error_cb.lock().unwrap())(C2Status::C2BadState); + return C2Status::C2BadState; + } + + let mut drain_job: J = Default::default(); + drain_job.set_drain(mode); + self.work_queue.lock().unwrap().push_back(drain_job); + + self.awaiting_job_event.write(1).unwrap(); + + C2Status::C2Ok + } +} + +// Instead of C2's release() function, we implement Drop and use RAII to +// accomplish the same thing +impl Drop for C2Wrapper +where + J: Send + Default + Job + 'static, + W: C2Worker, +{ + fn drop(&mut self) { + self.stop(); + } +} diff --git a/vendor/cros-codecs/src/c2_wrapper/c2_decoder.rs b/vendor/cros-codecs/src/c2_wrapper/c2_decoder.rs new file mode 100644 index 00000000..4d8f9156 --- /dev/null +++ b/vendor/cros-codecs/src/c2_wrapper/c2_decoder.rs @@ -0,0 +1,338 @@ +// Copyright 2025 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use nix::errno::Errno; +use nix::sys::epoll::Epoll; +use nix::sys::epoll::EpollCreateFlags; +use nix::sys::epoll::EpollEvent; +use nix::sys::epoll::EpollFlags; +use nix::sys::epoll::EpollTimeout; +use nix::sys::eventfd::EventFd; + +use std::collections::VecDeque; +use std::marker::PhantomData; +use std::os::fd::AsFd; +#[cfg(feature = "vaapi")] +use std::path::PathBuf; +use std::sync::Arc; +use std::sync::Mutex; +use thiserror::Error; + +use crate::c2_wrapper::C2DecodeJob; +use crate::c2_wrapper::C2State; +use crate::c2_wrapper::C2Status; +use crate::c2_wrapper::C2Worker; +use crate::c2_wrapper::DrainMode; +use crate::c2_wrapper::Job; +use crate::decoder::stateless::DecodeError; +use crate::decoder::stateless::DynStatelessVideoDecoder; +use crate::decoder::DecoderEvent; +use crate::decoder::StreamInfo; +use crate::image_processing::convert_video_frame; +use crate::video_frame::frame_pool::FramePool; +use crate::video_frame::frame_pool::PooledVideoFrame; +#[cfg(feature = "vaapi")] +use crate::video_frame::gbm_video_frame::{GbmDevice, GbmUsage, GbmVideoFrame}; +#[cfg(feature = "v4l2")] +use crate::video_frame::v4l2_mmap_video_frame::V4l2MmapVideoFrame; +use crate::video_frame::VideoFrame; +use crate::EncodedFormat; +use crate::Fourcc; + +#[derive(Debug, Error)] +pub enum C2DecoderPollErrorWrapper { + #[error("failed to create Epoll: {0}")] + Epoll(Errno), + #[error("failed to add poll FDs to Epoll: {0}")] + EpollAdd(Errno), +} + +pub trait C2DecoderBackend { + type DecoderOptions: Clone + Send + 'static; + + fn new(options: Self::DecoderOptions) -> Result + where + Self: Sized; + fn supported_output_formats(&self) -> Vec; + // TODO: Support stateful video decoders. + fn get_decoder( + &mut self, + input_format: EncodedFormat, + ) -> Result, String>; +} + +#[cfg(feature = "vaapi")] +type AuxiliaryVideoFrame = GbmVideoFrame; +#[cfg(feature = "v4l2")] +type AuxiliaryVideoFrame = V4l2MmapVideoFrame; + +// An "importing decoder" can directly import the DMA bufs we are getting, while a "converting +// decoder" is used for performing image processing routines to convert between the video hardware +// output and a pixel format that can be consumed by the GPU and display controller. +// TODO: Come up with a better name for these? +enum C2Decoder { + ImportingDecoder(DynStatelessVideoDecoder), + ConvertingDecoder(DynStatelessVideoDecoder>), +} + +pub struct C2DecoderWorker +where + V: VideoFrame, + B: C2DecoderBackend, +{ + decoder: C2Decoder, + epoll_fd: Epoll, + awaiting_job_event: Arc, + auxiliary_frame_pool: Option>, + error_cb: Arc>, + work_done_cb: Arc) + Send + 'static>>, + framepool_hint_cb: Arc>, + alloc_cb: Arc Option + Send + 'static>>, + work_queue: Arc>>>, + frame_num: u64, + state: Arc>, + _phantom: PhantomData, +} + +impl C2DecoderWorker +where + V: VideoFrame, + B: C2DecoderBackend, +{ + // Processes events from the decoder. Primarily these are frame decoded events and DRCs. + fn check_events(&mut self) { + loop { + let stream_info = match &self.decoder { + C2Decoder::ImportingDecoder(decoder) => decoder.stream_info().map(|x| x.clone()), + C2Decoder::ConvertingDecoder(decoder) => decoder.stream_info().map(|x| x.clone()), + }; + match &mut self.decoder { + C2Decoder::ImportingDecoder(decoder) => match decoder.next_event() { + Some(DecoderEvent::FrameReady(frame)) => { + frame.sync().unwrap(); + let mut job: C2DecodeJob = Default::default(); + job.output = Some(frame.video_frame()); + (*self.work_done_cb.lock().unwrap())(job); + } + Some(DecoderEvent::FormatChanged) => match stream_info { + Some(stream_info) => { + (*self.framepool_hint_cb.lock().unwrap())(stream_info.clone()); + } + None => { + log::debug!("Could not get stream info after format change!"); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + } + }, + _ => break, + }, + C2Decoder::ConvertingDecoder(decoder) => match decoder.next_event() { + Some(DecoderEvent::FrameReady(frame)) => { + frame.sync().unwrap(); + let mut job: C2DecodeJob = Default::default(); + let mut dst_frame = + (*self.alloc_cb.lock().unwrap())().expect("Allocation failed!"); + let src_frame = &*frame.video_frame(); + if let Err(err) = convert_video_frame(src_frame, &mut dst_frame) { + log::debug!("Error converting VideoFrame! {err}"); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + } + job.output = Some(Arc::new(dst_frame)); + (*self.work_done_cb.lock().unwrap())(job); + } + Some(DecoderEvent::FormatChanged) => match stream_info { + Some(stream_info) => { + (*self.framepool_hint_cb.lock().unwrap())(stream_info.clone()); + self.auxiliary_frame_pool.as_mut().unwrap().resize(&stream_info); + } + None => { + log::debug!("Could not get stream info after format change!"); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + } + }, + _ => break, + }, + } + } + } +} + +impl C2Worker> for C2DecoderWorker +where + V: VideoFrame, + B: C2DecoderBackend, +{ + type Options = ::DecoderOptions; + + fn new( + input_fourcc: Fourcc, + output_fourcc: Fourcc, + awaiting_job_event: Arc, + error_cb: Arc>, + work_done_cb: Arc) + Send + 'static>>, + work_queue: Arc>>>, + state: Arc>, + framepool_hint_cb: Arc>, + alloc_cb: Arc Option + Send + 'static>>, + options: Self::Options, + ) -> Result { + let mut backend = B::new(options)?; + let backend_fourccs = backend.supported_output_formats(); + let (auxiliary_frame_pool, decoder) = if backend_fourccs.contains(&output_fourcc) { + ( + None, + C2Decoder::ImportingDecoder( + backend.get_decoder(EncodedFormat::from(input_fourcc))?, + ), + ) + } else { + #[cfg(feature = "vaapi")] + { + let gbm_device = Arc::new( + GbmDevice::open(PathBuf::from("/dev/dri/renderD128")) + .expect("Could not open GBM device!"), + ); + let framepool = FramePool::new(move |stream_info: &StreamInfo| { + // TODO: Query the driver for these alignment params. + as Clone>::clone(&gbm_device) + .new_frame( + Fourcc::from(stream_info.format), + stream_info.display_resolution.clone(), + stream_info.coded_resolution.clone(), + GbmUsage::Decode, + ) + .expect("Could not allocate frame for auxiliary frame pool!") + }); + ( + Some(framepool), + C2Decoder::ConvertingDecoder( + backend.get_decoder(EncodedFormat::from(input_fourcc))?, + ), + ) + } + #[cfg(feature = "v4l2")] + { + let framepool = FramePool::new(move |stream_info: &StreamInfo| { + V4l2MmapVideoFrame::new( + Fourcc::from(stream_info.format), + stream_info.display_resolution.clone(), + ) + }); + ( + Some(framepool), + C2Decoder::ConvertingDecoder( + backend.get_decoder(EncodedFormat::from(input_fourcc))?, + ), + ) + } + }; + Ok(Self { + decoder: decoder, + auxiliary_frame_pool: auxiliary_frame_pool, + epoll_fd: Epoll::new(EpollCreateFlags::empty()) + .map_err(C2DecoderPollErrorWrapper::Epoll) + .unwrap(), + awaiting_job_event: awaiting_job_event, + error_cb: error_cb, + work_done_cb: work_done_cb, + framepool_hint_cb: framepool_hint_cb, + alloc_cb: alloc_cb, + work_queue: work_queue, + frame_num: 0, + state: state, + _phantom: Default::default(), + }) + } + + fn process_loop(&mut self) { + self.epoll_fd = Epoll::new(EpollCreateFlags::empty()) + .map_err(C2DecoderPollErrorWrapper::Epoll) + .unwrap(); + let _ = self + .epoll_fd + .add( + match &self.decoder { + C2Decoder::ImportingDecoder(decoder) => decoder.poll_fd(), + C2Decoder::ConvertingDecoder(decoder) => decoder.poll_fd(), + }, + EpollEvent::new(EpollFlags::EPOLLIN, 1), + ) + .map_err(C2DecoderPollErrorWrapper::EpollAdd); + self.epoll_fd + .add(self.awaiting_job_event.as_fd(), EpollEvent::new(EpollFlags::EPOLLIN, 2)) + .map_err(C2DecoderPollErrorWrapper::EpollAdd) + .unwrap(); + + while *self.state.lock().unwrap() == C2State::C2Running { + // Poll for decoder events or pending job events. + let mut events = [EpollEvent::empty()]; + let _nb_fds = self.epoll_fd.wait(&mut events, EpollTimeout::NONE).unwrap(); + + if events == [EpollEvent::new(EpollFlags::EPOLLIN, 2)] { + self.awaiting_job_event.read().unwrap(); + } + + // We want to try sending compressed buffers to the decoder regardless of what event + // woke us up, because we either have new work, or we might more output buffers + // available. + let mut possible_job = (*self.work_queue.lock().unwrap()).pop_front(); + while let Some(mut job) = possible_job { + if job.get_drain() != DrainMode::NoDrain { + let flush_result = match &mut self.decoder { + C2Decoder::ImportingDecoder(decoder) => decoder.flush(), + C2Decoder::ConvertingDecoder(decoder) => decoder.flush(), + }; + if let Err(_) = flush_result { + log::debug!("Error handling drain request!"); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + } else { + if job.get_drain() == DrainMode::EOSDrain { + *self.state.lock().unwrap() = C2State::C2Stopped; + } + self.check_events(); + } + break; + } else { + let bitstream = job.input.as_slice(); + let decode_result = match &mut self.decoder { + C2Decoder::ImportingDecoder(decoder) => decoder.decode( + self.frame_num, + bitstream, + &mut *self.alloc_cb.lock().unwrap(), + ), + C2Decoder::ConvertingDecoder(decoder) => { + decoder.decode(self.frame_num, bitstream, &mut || { + self.auxiliary_frame_pool.as_mut().unwrap().alloc() + }) + } + }; + match decode_result { + Ok(num_bytes) => { + self.frame_num += 1; + if num_bytes != job.input.len() { + job.input = (&job.input[num_bytes..]).to_vec(); + (*self.work_queue.lock().unwrap()).push_front(job); + } + } + Err(DecodeError::NotEnoughOutputBuffers(_) | DecodeError::CheckEvents) => { + (*self.work_queue.lock().unwrap()).push_front(job); + break; + } + Err(e) => { + log::debug!("Unhandled error message from decoder {e:?}"); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + break; + } + } + } + possible_job = (*self.work_queue.lock().unwrap()).pop_front(); + } + self.check_events(); + } + } +} diff --git a/vendor/cros-codecs/src/c2_wrapper/c2_encoder.rs b/vendor/cros-codecs/src/c2_wrapper/c2_encoder.rs new file mode 100644 index 00000000..d21bd451 --- /dev/null +++ b/vendor/cros-codecs/src/c2_wrapper/c2_encoder.rs @@ -0,0 +1,272 @@ +// Copyright 2025 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use nix::sys::epoll::Epoll; +use nix::sys::epoll::EpollCreateFlags; +use nix::sys::epoll::EpollEvent; +use nix::sys::epoll::EpollFlags; +use nix::sys::epoll::EpollTimeout; +use nix::sys::eventfd::EventFd; + +use std::collections::VecDeque; +use std::marker::PhantomData; +use std::os::fd::AsFd; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::sync::Mutex; +use std::time::Duration; + +use crate::c2_wrapper::C2EncodeJob; +use crate::c2_wrapper::C2State; +use crate::c2_wrapper::C2Status; +use crate::c2_wrapper::C2Worker; +use crate::c2_wrapper::DrainMode; +use crate::c2_wrapper::Job; +use crate::decoder::StreamInfo; +use crate::encoder::FrameMetadata; +use crate::encoder::RateControl; +use crate::encoder::RateControl::ConstantBitrate; +use crate::encoder::RateControl::ConstantQuality; +use crate::encoder::Tunings; +use crate::encoder::VideoEncoder; +use crate::image_processing::convert_video_frame; +use crate::image_processing::extend_border_nv12; +#[cfg(feature = "v4l2")] +use crate::video_frame::V4l2VideoFrame; +use crate::video_frame::VideoFrame; +use crate::video_frame::UV_PLANE; +use crate::video_frame::Y_PLANE; +use crate::DecodedFormat; +use crate::EncodedFormat; +use crate::Fourcc; +use crate::Resolution; + +pub trait C2EncoderBackend { + type EncoderOptions: Clone + Send + 'static; + + fn new(options: Self::EncoderOptions) -> Result + where + Self: Sized; + + // Returns a new encoder, the visible resolution, and the coded resolution. + #[cfg(feature = "vaapi")] + fn get_encoder( + &mut self, + input_format: DecodedFormat, + output_format: EncodedFormat, + ) -> Result<(Box>, Resolution, Resolution), String>; + #[cfg(feature = "v4l2")] + fn get_encoder( + &mut self, + input_format: DecodedFormat, + output_format: EncodedFormat, + ) -> Result<(Box>>, Resolution, Resolution), String>; +} + +pub struct C2EncoderWorker +where + V: VideoFrame, + B: C2EncoderBackend, +{ + #[cfg(feature = "vaapi")] + encoder: Box>, + #[cfg(feature = "v4l2")] + encoder: Box>>, + awaiting_job_event: Arc, + error_cb: Arc>, + work_done_cb: Arc) + Send + 'static>>, + alloc_cb: Arc Option + Send + 'static>>, + work_queue: Arc>>>, + in_flight_queue: VecDeque>, + state: Arc>, + current_tunings: Tunings, + visible_resolution: Resolution, + coded_resolution: Resolution, + _phantom: PhantomData, +} + +impl C2EncoderWorker +where + V: VideoFrame, + B: C2EncoderBackend, +{ + fn poll_complete_frames(&mut self) { + loop { + match self.encoder.poll() { + Ok(Some(coded)) => { + let mut job = self.in_flight_queue.pop_front().unwrap(); + job.output = coded.bitstream; + (*self.work_done_cb.lock().unwrap())(job); + } + Err(err) => { + log::debug!("Error during encode! {:?}", err); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + break; + } + _ => break, + } + } + } +} + +impl C2Worker> for C2EncoderWorker +where + V: VideoFrame, + B: C2EncoderBackend, +{ + type Options = B::EncoderOptions; + + fn new( + input_fourcc: Fourcc, + output_fourcc: Fourcc, + awaiting_job_event: Arc, + error_cb: Arc>, + work_done_cb: Arc) + Send + 'static>>, + work_queue: Arc>>>, + state: Arc>, + framepool_hint_cb: Arc>, + alloc_cb: Arc Option + Send + 'static>>, + options: Self::Options, + ) -> Result { + let mut backend = B::new(options)?; + let (encoder, visible_resolution, coded_resolution) = backend + .get_encoder(DecodedFormat::from(input_fourcc), EncodedFormat::from(output_fourcc))?; + (*framepool_hint_cb.lock().unwrap())(StreamInfo { + format: DecodedFormat::from(input_fourcc), + coded_resolution: coded_resolution.clone(), + display_resolution: visible_resolution.clone(), + // Needs to be equal to the pipeline depth, which is decided by the client. Ideally, we + // will never need these temp frames though if Gralloc works properly. + min_num_frames: 0, + }); + + Ok(Self { + encoder: encoder, + awaiting_job_event: awaiting_job_event, + error_cb: error_cb, + work_done_cb: work_done_cb, + work_queue: work_queue, + in_flight_queue: VecDeque::new(), + state: state, + alloc_cb: alloc_cb, + current_tunings: Default::default(), + visible_resolution: visible_resolution, + coded_resolution: coded_resolution, + _phantom: Default::default(), + }) + } + + fn process_loop(&mut self) { + let epoll_fd = Epoll::new(EpollCreateFlags::empty()).expect("Failed to create Epoll"); + let _ = epoll_fd + .add(self.awaiting_job_event.as_fd(), EpollEvent::new(EpollFlags::EPOLLIN, 1)) + .expect("Failed to add job event to Epoll"); + + while *self.state.lock().unwrap() == C2State::C2Running { + let mut events = [EpollEvent::empty()]; + // We need an actual timeout because the encoder is poll based rather than async. + let _ = epoll_fd + .wait(&mut events, EpollTimeout::try_from(Duration::from_millis(10)).unwrap()) + .expect("Epoll wait failed"); + if events != [EpollEvent::new(EpollFlags::EPOLLIN, 1)] { + self.poll_complete_frames(); + continue; + } + + self.awaiting_job_event.read().unwrap(); + + // Unlike the decoder, we can assume a 1:1 relationship between jobs in the work_queue + // and wake-up events. The encoders are not asynchronous, so the only events we will + // ever wake-up for are jobs being added to the work_queue. + let mut job = (*self.work_queue.lock().unwrap()) + .pop_front() + .expect("Missing job from work queue!"); + if job.get_drain() != DrainMode::NoDrain { + if let Err(err) = self.encoder.drain() { + log::debug!("Error draining encoder! {:?}", err); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + break; + } + if job.get_drain() == DrainMode::EOSDrain { + *self.state.lock().unwrap() = C2State::C2Stopped; + } + } else { + let frame_y_stride = job.input.as_ref().unwrap().get_plane_pitch()[0]; + let frame_y_size = job.input.as_ref().unwrap().get_plane_size()[0]; + let can_import_frame = frame_y_stride == self.coded_resolution.width as usize + && frame_y_size >= self.coded_resolution.get_area(); + let frame = if can_import_frame { + job.input.take().unwrap() + } else { + let mut tmp = (*self.alloc_cb.lock().unwrap())().expect("Allocation failed!"); + + if let Err(_) = convert_video_frame(job.input.as_ref().unwrap(), &mut tmp) { + log::debug!("Failed to copy input frame to properly aligned buffer!"); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + break; + } + { + let tmp_mapping = tmp.map_mut().expect("Failed to map tmp frame!"); + let tmp_planes = tmp_mapping.get(); + extend_border_nv12( + *tmp_planes[Y_PLANE].borrow_mut(), + *tmp_planes[UV_PLANE].borrow_mut(), + self.visible_resolution.width as usize, + self.visible_resolution.height as usize, + self.coded_resolution.width as usize, + self.coded_resolution.height as usize, + ); + } + + tmp + }; + + let curr_bitrate = match self.current_tunings.rate_control { + ConstantBitrate(bitrate) => bitrate, + ConstantQuality(_) => { + log::debug!("CQ encoding not currently supported"); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + break; + } + }; + let new_framerate = job.framerate.load(Ordering::Relaxed); + if job.bitrate != curr_bitrate || new_framerate != self.current_tunings.framerate { + self.current_tunings.rate_control = RateControl::ConstantBitrate(job.bitrate); + self.current_tunings.framerate = new_framerate; + if let Err(err) = self.encoder.tune(self.current_tunings.clone()) { + log::debug!("Error adjusting tunings! {:?}", err); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + break; + } + }; + + let meta = FrameMetadata { + timestamp: job.timestamp, + layout: Default::default(), + force_keyframe: false, + }; + #[cfg(feature = "vaapi")] + let encode_result = self.encoder.encode(meta, frame); + #[cfg(feature = "v4l2")] + let encode_result = self.encoder.encode(meta, V4l2VideoFrame(frame)); + match encode_result { + Ok(_) => self.in_flight_queue.push_back(job), + Err(err) => { + log::debug!("Error encoding frame! {:?}", err); + *self.state.lock().unwrap() = C2State::C2Error; + (*self.error_cb.lock().unwrap())(C2Status::C2BadValue); + break; + } + } + } + + self.poll_complete_frames(); + } + } +} diff --git a/vendor/cros-codecs/src/c2_wrapper/c2_v4l2_decoder.rs b/vendor/cros-codecs/src/c2_wrapper/c2_v4l2_decoder.rs new file mode 100644 index 00000000..7858d24d --- /dev/null +++ b/vendor/cros-codecs/src/c2_wrapper/c2_v4l2_decoder.rs @@ -0,0 +1,60 @@ +// Copyright 2025 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::path::PathBuf; + +use crate::c2_wrapper::c2_decoder::C2DecoderBackend; +use crate::decoder::stateless::av1::Av1; +use crate::decoder::stateless::h264::H264; +use crate::decoder::stateless::vp8::Vp8; +use crate::decoder::stateless::vp9::Vp9; +use crate::decoder::stateless::DynStatelessVideoDecoder; +use crate::decoder::stateless::StatelessDecoder; +use crate::decoder::stateless::StatelessVideoDecoder; +use crate::decoder::BlockingMode; +use crate::video_frame::VideoFrame; +use crate::EncodedFormat; +use crate::Fourcc; + +#[derive(Clone, Debug)] +pub struct C2V4L2DecoderOptions { + // TODO: This is currently unused, but we should plumb it to V4L2Device initialization. + pub video_device_path: Option, +} + +pub struct C2V4L2Decoder {} + +impl C2DecoderBackend for C2V4L2Decoder { + type DecoderOptions = C2V4L2DecoderOptions; + + fn new(_options: C2V4L2DecoderOptions) -> Result { + Ok(Self {}) + } + + // TODO: Actually query the driver for this information. + fn supported_output_formats(&self) -> Vec { + vec![Fourcc::from(b"MM21")] + } + + fn get_decoder( + &mut self, + format: EncodedFormat, + ) -> Result, String> { + Ok(match format { + EncodedFormat::AV1 => StatelessDecoder::::new_v4l2(BlockingMode::NonBlocking) + .map_err(|_| "Failed to instantiate AV1 decoder")? + .into_trait_object(), + EncodedFormat::H264 => StatelessDecoder::::new_v4l2(BlockingMode::NonBlocking) + .map_err(|_| "Failed to instantiate H264 decoder")? + .into_trait_object(), + EncodedFormat::VP8 => StatelessDecoder::::new_v4l2(BlockingMode::NonBlocking) + .map_err(|_| "Failed to instantiate VP8 decoder")? + .into_trait_object(), + EncodedFormat::VP9 => StatelessDecoder::::new_v4l2(BlockingMode::NonBlocking) + .map_err(|_| "Failed to instantiate VP9 decoder")? + .into_trait_object(), + _ => return Err(format!("Unsupported format {format:?}")), + }) + } +} diff --git a/vendor/cros-codecs/src/c2_wrapper/c2_v4l2_encoder.rs b/vendor/cros-codecs/src/c2_wrapper/c2_v4l2_encoder.rs new file mode 100644 index 00000000..edc8ce29 --- /dev/null +++ b/vendor/cros-codecs/src/c2_wrapper/c2_v4l2_encoder.rs @@ -0,0 +1,131 @@ +// Copyright 2025 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::sync::Arc; + +use crate::backend::v4l2::encoder::find_device_with_capture; +use crate::backend::v4l2::encoder::MmapingCapture; +use crate::c2_wrapper::c2_encoder::C2EncoderBackend; +use crate::encoder::stateful::h264::v4l2::V4L2StatefulH264Encoder; +use crate::encoder::stateful::vp8::v4l2::V4L2StatefulVP8Encoder; +use crate::encoder::stateful::vp9::v4l2::V4L2StatefulVP9Encoder; +use crate::encoder::VideoEncoder; +use crate::video_frame::V4l2VideoFrame; +use crate::video_frame::VideoFrame; +use crate::DecodedFormat; +use crate::EncodedFormat; +use crate::Fourcc; +use crate::Resolution; + +use v4l2r::device::Device; +use v4l2r::device::DeviceConfig; + +fn v4l2_format_to_coded_size(format: &v4l2r::Format) -> Resolution { + let stride = format.plane_fmt[0].bytesperline; + let size = format.plane_fmt[0].sizeimage; + Resolution { width: stride, height: size / stride } +} + +#[derive(Clone, Debug)] +pub struct C2V4L2EncoderOptions { + pub output_fourcc: Fourcc, + pub visible_resolution: Resolution, +} + +pub struct C2V4L2Encoder { + visible_resolution: Resolution, + device: Arc, +} + +impl C2EncoderBackend for C2V4L2Encoder { + type EncoderOptions = C2V4L2EncoderOptions; + + fn new(options: C2V4L2EncoderOptions) -> Result { + let pixel_format = v4l2r::PixelFormat::from_u32(u32::from(options.output_fourcc)); + let device = find_device_with_capture(pixel_format) + .ok_or("Could not find V4L2 device!".to_string())?; + let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()) + .map_err(|err| format!("Error opening V4L2 device! {:?}", err))?; + Ok(Self { visible_resolution: options.visible_resolution, device: Arc::new(device) }) + } + + fn get_encoder( + &mut self, + input_format: DecodedFormat, + output_format: EncodedFormat, + ) -> Result<(Box>>, Resolution, Resolution), String> { + Ok(match output_format { + EncodedFormat::H264 => { + let mut encoder = V4L2StatefulH264Encoder::new( + self.device.clone(), + MmapingCapture, + crate::encoder::h264::EncoderConfig { + resolution: self.visible_resolution.clone(), + ..Default::default() + }, + Fourcc::from(input_format), + self.visible_resolution.clone(), + Default::default(), + ) + .map_err(|err| format!("Error initializing encoder! {:?}", err))?; + let coded_format = encoder + .backend() + .output_format() + .map_err(|err| format!("Error querying backend format! {:?}", err))?; + ( + Box::new(encoder), + self.visible_resolution.clone(), + v4l2_format_to_coded_size(&coded_format), + ) + } + EncodedFormat::VP8 => { + let mut encoder = V4L2StatefulVP8Encoder::new( + self.device.clone(), + MmapingCapture, + crate::encoder::vp8::EncoderConfig { + resolution: self.visible_resolution.clone(), + ..Default::default() + }, + Fourcc::from(input_format), + self.visible_resolution.clone(), + Default::default(), + ) + .map_err(|err| format!("Error initializing encoder! {:?}", err))?; + let coded_format = encoder + .backend() + .output_format() + .map_err(|err| format!("Error querying backend format! {:?}", err))?; + ( + Box::new(encoder), + self.visible_resolution.clone(), + v4l2_format_to_coded_size(&coded_format), + ) + } + EncodedFormat::VP9 => { + let mut encoder = V4L2StatefulVP9Encoder::new( + self.device.clone(), + MmapingCapture, + crate::encoder::vp9::EncoderConfig { + resolution: self.visible_resolution.clone(), + ..Default::default() + }, + Fourcc::from(input_format), + self.visible_resolution.clone(), + Default::default(), + ) + .map_err(|err| format!("Error initializing encoder! {:?}", err))?; + let coded_format = encoder + .backend() + .output_format() + .map_err(|err| format!("Error querying backend format! {:?}", err))?; + ( + Box::new(encoder), + self.visible_resolution.clone(), + v4l2_format_to_coded_size(&coded_format), + ) + } + _ => return Err(format!("Format not supported by V4L2! {:?}", output_format)), + }) + } +} diff --git a/vendor/cros-codecs/src/c2_wrapper/c2_vaapi_decoder.rs b/vendor/cros-codecs/src/c2_wrapper/c2_vaapi_decoder.rs new file mode 100644 index 00000000..6f234f34 --- /dev/null +++ b/vendor/cros-codecs/src/c2_wrapper/c2_vaapi_decoder.rs @@ -0,0 +1,86 @@ +// Copyright 2025 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::path::PathBuf; +use std::rc::Rc; + +use crate::c2_wrapper::c2_decoder::C2DecoderBackend; +use crate::decoder::stateless::av1::Av1; +use crate::decoder::stateless::h264::H264; +use crate::decoder::stateless::h265::H265; +use crate::decoder::stateless::vp8::Vp8; +use crate::decoder::stateless::vp9::Vp9; +use crate::decoder::stateless::DynStatelessVideoDecoder; +use crate::decoder::stateless::StatelessDecoder; +use crate::decoder::stateless::StatelessVideoDecoder; +use crate::decoder::BlockingMode; +use crate::video_frame::VideoFrame; +use crate::EncodedFormat; +use crate::Fourcc; + +#[derive(Clone, Debug)] +pub struct C2VaapiDecoderOptions { + pub libva_device_path: Option, +} + +pub struct C2VaapiDecoder { + display: Rc, +} + +impl C2DecoderBackend for C2VaapiDecoder { + type DecoderOptions = C2VaapiDecoderOptions; + + fn new(options: C2VaapiDecoderOptions) -> Result { + let display = match options.libva_device_path { + Some(libva_device_path) => libva::Display::open_drm_display(libva_device_path.clone()) + .map_err(|_| format!("failed to open libva display {libva_device_path:?}"))?, + None => libva::Display::open().ok_or("failed to open libva display")?, + }; + + Ok(Self { display: display }) + } + + // TODO: Actually query the driver for this information. + fn supported_output_formats(&self) -> Vec { + vec![Fourcc::from(b"NV12")] + } + + fn get_decoder( + &mut self, + format: EncodedFormat, + ) -> Result, String> { + Ok(match format { + EncodedFormat::H264 => StatelessDecoder::::new_vaapi( + self.display.clone(), + BlockingMode::NonBlocking, + ) + .map_err(|_| "Failed to instantiate H264 encoder")? + .into_trait_object(), + EncodedFormat::H265 => StatelessDecoder::::new_vaapi( + self.display.clone(), + BlockingMode::NonBlocking, + ) + .map_err(|_| "Failed to instantiate H265 encoder")? + .into_trait_object(), + EncodedFormat::VP8 => StatelessDecoder::::new_vaapi( + self.display.clone(), + BlockingMode::NonBlocking, + ) + .map_err(|_| "Failed to instantiate VP8 encoder")? + .into_trait_object(), + EncodedFormat::VP9 => StatelessDecoder::::new_vaapi( + self.display.clone(), + BlockingMode::NonBlocking, + ) + .map_err(|_| "Failed to instantiate VP9 encoder")? + .into_trait_object(), + EncodedFormat::AV1 => StatelessDecoder::::new_vaapi( + self.display.clone(), + BlockingMode::NonBlocking, + ) + .map_err(|_| "Failed to instantiate AV1 encoder")? + .into_trait_object(), + }) + } +} diff --git a/vendor/cros-codecs/src/c2_wrapper/c2_vaapi_encoder.rs b/vendor/cros-codecs/src/c2_wrapper/c2_vaapi_encoder.rs new file mode 100644 index 00000000..d2856925 --- /dev/null +++ b/vendor/cros-codecs/src/c2_wrapper/c2_vaapi_encoder.rs @@ -0,0 +1,112 @@ +// Copyright 2025 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::rc::Rc; + +use crate::c2_wrapper::c2_encoder::C2EncoderBackend; +use crate::encoder::av1::EncoderConfig as AV1EncoderConfig; +use crate::encoder::h264::EncoderConfig as H264EncoderConfig; +use crate::encoder::stateless::av1; +use crate::encoder::stateless::h264; +use crate::encoder::stateless::vp9; +use crate::encoder::vp9::EncoderConfig as VP9EncoderConfig; +use crate::encoder::VideoEncoder; +use crate::utils::align_up; +use crate::video_frame::VideoFrame; +use crate::BlockingMode; +use crate::DecodedFormat; +use crate::EncodedFormat; +use crate::Fourcc; +use crate::Resolution; + +#[derive(Clone, Debug)] +pub struct C2VaapiEncoderOptions { + pub low_power: bool, + pub visible_resolution: Resolution, +} + +pub struct C2VaapiEncoder { + display: Rc, + low_power: bool, + visible_resolution: Resolution, + coded_resolution: Resolution, +} + +impl C2EncoderBackend for C2VaapiEncoder { + type EncoderOptions = C2VaapiEncoderOptions; + + fn new(options: C2VaapiEncoderOptions) -> Result { + const VAAPI_WIDTH_ALIGN: u32 = 16; + const VAAPI_HEIGHT_ALIGN: u32 = 16; + + // TODO: Support alternative display paths + let display = libva::Display::open().ok_or("Error opening LibVA display!".to_string())?; + Ok(Self { + display: display, + low_power: options.low_power, + visible_resolution: options.visible_resolution.clone(), + // TODO: This really shouldn't be necessary, but for some reason minigbm gets the + // vertical alignment wrong when we omit it :( + coded_resolution: Resolution { + width: align_up(options.visible_resolution.width, VAAPI_WIDTH_ALIGN), + height: align_up(options.visible_resolution.height, VAAPI_HEIGHT_ALIGN), + }, + }) + } + + fn get_encoder( + &mut self, + input_format: DecodedFormat, + output_format: EncodedFormat, + ) -> Result<(Box>, Resolution, Resolution), String> { + Ok(match output_format { + EncodedFormat::H264 => { + let encoder = h264::StatelessEncoder::new_vaapi( + self.display.clone(), + H264EncoderConfig { + resolution: self.visible_resolution.clone(), + ..Default::default() + }, + Fourcc::from(input_format), + self.visible_resolution.clone(), + self.low_power, + BlockingMode::Blocking, + ) + .map_err(|err| format!("Error initializing encoder! {:?}", err))?; + (Box::new(encoder), self.visible_resolution.clone(), self.coded_resolution.clone()) + } + EncodedFormat::VP9 => { + let encoder = vp9::StatelessEncoder::new_vaapi( + self.display.clone(), + VP9EncoderConfig { + resolution: self.visible_resolution.clone(), + ..Default::default() + }, + Fourcc::from(input_format), + self.visible_resolution.clone(), + self.low_power, + BlockingMode::Blocking, + ) + .map_err(|err| format!("Error initializing encoder! {:?}", err))?; + (Box::new(encoder), self.visible_resolution.clone(), self.coded_resolution.clone()) + } + EncodedFormat::AV1 => { + let encoder = av1::StatelessEncoder::new_vaapi( + self.display.clone(), + AV1EncoderConfig { + resolution: self.visible_resolution.clone(), + ..Default::default() + }, + Fourcc::from(input_format), + self.visible_resolution.clone(), + self.low_power, + BlockingMode::Blocking, + ) + .map_err(|err| format!("Error initializing encoder! {:?}", err))?; + (Box::new(encoder), self.visible_resolution.clone(), self.coded_resolution.clone()) + } + _ => return Err(format!("Format not supported by V4L2! {:?}", output_format)), + }) + } +} diff --git a/vendor/cros-codecs/src/codec.rs b/vendor/cros-codecs/src/codec.rs new file mode 100644 index 00000000..582ef5fc --- /dev/null +++ b/vendor/cros-codecs/src/codec.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! Parsers for various kinds of encoded streams. +//! +//! This module does not provide any actual decoding tools - that's the job of the +//! [crate::decoder] module. However the parsers of this module are heavily used in order to +//! implement stateless decoding. +//! +//! There shall be no dependencies from other modules of this crate to this module, so that it +//! can be turned into a crate of its own if needed in the future. + +pub mod av1; +pub mod h264; +pub mod h265; +pub mod vp8; +pub mod vp9; diff --git a/vendor/cros-codecs/src/codec/av1.rs b/vendor/cros-codecs/src/codec/av1.rs new file mode 100644 index 00000000..477cd906 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1.rs @@ -0,0 +1,9 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +mod helpers; +pub mod parser; +pub mod reader; +pub mod synthesizer; +pub mod writer; diff --git a/vendor/cros-codecs/src/codec/av1/helpers.rs b/vendor/cros-codecs/src/codec/av1/helpers.rs new file mode 100644 index 00000000..ccf65b49 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/helpers.rs @@ -0,0 +1,178 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::codec::av1::parser::NUM_REF_FRAMES; + +const DIV_LUT: [i32; 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, +]; + +const DIV_LUT_BITS: u32 = 8; +const DIV_LUT_PREC_BITS: u32 = 14; + +/// Implements FloorLog2(x), which is defined to be the floor of the base 2 +/// logarithm of the input x. +/// +/// The input x will always be an integer, and will always be greater than or equal to 1. +/// This function extracts the location of the most significant bit in x. +pub fn floor_log2(mut x: u32) -> u32 { + assert!(x > 0); + let mut s = 0; + + while x != 0 { + x >>= 1; + s += 1; + } + + s - 1 +} + +/// Implements 5.9.3. Get relative distance function +pub fn get_relative_dist(enable_order_hint: bool, order_hint_bits: i32, a: i32, b: i32) -> i32 { + if !enable_order_hint { + 0 + } else { + let diff = a - b; + let m = 1 << (order_hint_bits - 1); + (diff & (m - 1)) - (diff & m) + } +} + +/// Implements find_latest_backward from section 7.8. +pub fn find_latest_backward( + shifted_order_hints: &[i32; NUM_REF_FRAMES], + used_frame: &[bool; NUM_REF_FRAMES], + cur_frame_hint: i32, + latest_order_hint: &mut i32, +) -> i32 { + let mut _ref = -1; + + for i in 0..NUM_REF_FRAMES { + let hint = shifted_order_hints[i]; + if !used_frame[i] && hint >= cur_frame_hint && (_ref < 0 || hint >= *latest_order_hint) { + _ref = i as i32; + *latest_order_hint = hint; + } + } + + _ref +} + +/// Implements find_earliest_backward from section 7.8. +pub fn find_earliest_backward( + shifted_order_hints: &[i32; NUM_REF_FRAMES], + used_frame: &[bool; NUM_REF_FRAMES], + cur_frame_hint: i32, + earliest_order_hint: &mut i32, +) -> i32 { + let mut _ref = -1; + + for i in 0..NUM_REF_FRAMES { + let hint = shifted_order_hints[i]; + if !used_frame[i] && hint >= cur_frame_hint && (_ref < 0 || hint < *earliest_order_hint) { + _ref = i as i32; + *earliest_order_hint = hint; + } + } + + _ref +} + +/// Implements find_latest_forward from section 7.8. +pub fn find_latest_forward( + shifted_order_hints: &[i32; NUM_REF_FRAMES], + used_frame: &[bool; NUM_REF_FRAMES], + cur_frame_hint: i32, + latest_order_hint: &mut i32, +) -> i32 { + let mut _ref = -1; + for i in 0..NUM_REF_FRAMES { + let hint = shifted_order_hints[i]; + if !used_frame[i] && hint < cur_frame_hint && (_ref < 0 || hint >= *latest_order_hint) { + _ref = i as i32; + *latest_order_hint = hint; + } + } + + _ref +} + +pub fn tile_log2(blk_size: u32, target: u32) -> u32 { + let mut k = 0; + + while (blk_size << k) < target { + k += 1; + } + + k +} + +pub fn clip3(x: i32, y: i32, z: i32) -> i32 { + if z < x { + x + } else if z > y { + y + } else { + z + } +} + +/// 5.9.29 +pub fn inverse_recenter(r: i32, v: i32) -> i32 { + if v > 2 * r { + v + } else if v & 1 != 0 { + r - ((v + 1) >> 1) + } else { + r + (v >> 1) + } +} + +/// Implements Round2. See 4.7: mathematical functions. +pub fn round2(x: u32, n: u32) -> u32 { + (x + 2u32.pow(n - 1)) / 2u32.pow(n) +} + +/// Implements Round2Signed. See 4.7: mathematical functions. +pub fn round2signed(x: i32, n: u32) -> Result { + if x >= 0 { + i32::try_from(round2(x as u32, n)).map_err(|e| e.to_string()) + } else { + let x = x as i64; + let val = i32::try_from(round2(-x as u32, n)).map_err(|e| e.to_string())?; + Ok(-val) + } +} + +/// Implements 7.11.3.7. Resolve divisor process +pub fn resolve_divisor(d: i32) -> Result<(u32, i32), String> { + let abs_d = u32::try_from(d.abs()).unwrap(); // abs cannot return a negative + let n = floor_log2(abs_d); + let e = abs_d - (1 << n); + + let f = if n > DIV_LUT_BITS { round2(e, n - DIV_LUT_BITS) } else { e << (DIV_LUT_BITS - n) }; + + let div_shift = n + DIV_LUT_PREC_BITS; + let div_factor = if d < 0 { -DIV_LUT[f as usize] } else { DIV_LUT[f as usize] }; + + Ok((div_shift, div_factor)) +} diff --git a/vendor/cros-codecs/src/codec/av1/parser.rs b/vendor/cros-codecs/src/codec/av1/parser.rs new file mode 100644 index 00000000..b059a223 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/parser.rs @@ -0,0 +1,4216 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::borrow::Cow; +use std::rc::Rc; + +use crate::codec::av1::helpers; +use crate::codec::av1::reader::Reader; + +pub const TOTAL_REFS_PER_FRAME: usize = 8; +pub const NUM_REF_FRAMES: usize = 8; +pub const REFS_PER_FRAME: usize = 7; +pub const MAX_SEGMENTS: usize = 8; +pub const SEG_LVL_ALT_Q: usize = 0; +pub const SEG_LVL_ALT_LF_Y_V: usize = 1; +pub const SEG_LVL_REF_FRAME: usize = 5; +pub const SEG_LVL_SKIP: usize = 6; +pub const SEG_LVL_GLOBAL_MV: usize = 7; +pub const SEG_LVL_MAX: usize = 8; +pub const MAX_TILE_COLS: usize = 64; +pub const MAX_TILE_ROWS: usize = 64; +pub const CDEF_MAX: usize = 1 << 3; +pub const MAX_NUM_PLANES: usize = 3; +pub const MAX_NUM_Y_POINTS: usize = 16; +pub const MAX_NUM_CB_POINTS: usize = 16; +pub const MAX_NUM_CR_POINTS: usize = 16; +pub const MAX_NUM_POS_LUMA: usize = 25; +pub const MAX_NUM_SPATIAL_LAYERS: usize = 4; +pub const MAX_NUM_TEMPORAL_LAYERS: usize = 8; +pub const MAX_NUM_OPERATING_POINTS: usize = MAX_NUM_SPATIAL_LAYERS * MAX_NUM_TEMPORAL_LAYERS; +pub const SELECT_SCREEN_CONTENT_TOOLS: usize = 2; +pub const SELECT_INTEGER_MV: usize = 2; +pub const PRIMARY_REF_NONE: u32 = 7; +pub const SUPERRES_DENOM_BITS: usize = 3; +pub const SUPERRES_DENOM_MIN: usize = 9; +pub const SUPERRES_NUM: usize = 8; +pub const MAX_TILE_WIDTH: u32 = 4096; +pub const MAX_TILE_HEIGHT: u32 = 2304; +pub const MAX_TILE_AREA: u32 = MAX_TILE_WIDTH * MAX_TILE_HEIGHT; +pub const RESTORATION_TILESIZE_MAX: u16 = 256; +pub const WARPEDMODEL_PREC_BITS: u32 = 16; +pub const WARP_PARAM_REDUCE_BITS: u32 = 6; +pub const GM_ABS_ALPHA_BITS: u32 = 12; +pub const GM_ALPHA_PREC_BITS: u32 = 15; +pub const GM_ABS_TRANS_ONLY_BITS: u32 = 9; +pub const GM_TRANS_ONLY_PREC_BITS: u32 = 3; +pub const GM_ABS_TRANS_BITS: u32 = 12; +pub const GM_TRANS_PREC_BITS: u32 = 6; + +// Same as Segmentation_Feature_Bits in the specification. See 5.9.14 +pub const FEATURE_BITS: [u8; SEG_LVL_MAX] = [8, 6, 6, 6, 6, 3, 0, 0]; +// Same as Segmentation_Feature_Signed in the specification. See 5.9.14 +pub const FEATURE_SIGNED: [bool; SEG_LVL_MAX] = [true, true, true, true, true, false, false, false]; +// Same as Segmentation_Feature_Max in the specification. See 5.9.14 +pub const FEATURE_MAX: [i32; SEG_LVL_MAX] = [255, 63, 63, 63, 63, 7, 0, 0]; + +/// Tells what should be done with the OBU after [`Parser::read_obu`] is called. +pub enum ObuAction<'a> { + /// We should process the OBU normally. + Process(Obu<'a>), + /// We should drop this OBU and advance to the next one. The u32 is how much + /// we should advance. + Drop(u32), +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum ObuType { + #[default] + Reserved = 0, + SequenceHeader = 1, + TemporalDelimiter = 2, + FrameHeader = 3, + TileGroup = 4, + Metadata = 5, + Frame = 6, + RedundantFrameHeader = 7, + TileList = 8, + Reserved2 = 9, + Reserved3 = 10, + Reserved4 = 11, + Reserved5 = 12, + Reserved6 = 13, + Reserved7 = 14, + Padding = 15, +} + +impl TryFrom for ObuType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ObuType::Reserved), + 1 => Ok(ObuType::SequenceHeader), + 2 => Ok(ObuType::TemporalDelimiter), + 3 => Ok(ObuType::FrameHeader), + 4 => Ok(ObuType::TileGroup), + 5 => Ok(ObuType::Metadata), + 6 => Ok(ObuType::Frame), + 7 => Ok(ObuType::RedundantFrameHeader), + 8 => Ok(ObuType::TileList), + 9 => Ok(ObuType::Reserved2), + 10 => Ok(ObuType::Reserved3), + 11 => Ok(ObuType::Reserved4), + 12 => Ok(ObuType::Reserved5), + 13 => Ok(ObuType::Reserved6), + 14 => Ok(ObuType::Reserved7), + 15 => Ok(ObuType::Padding), + _ => Err(format!("Invalid ObuType {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum Profile { + #[default] + Profile0 = 0, + Profile1 = 1, + Profile2 = 2, +} + +impl TryFrom for Profile { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(Profile::Profile0), + 1 => Ok(Profile::Profile1), + 2 => Ok(Profile::Profile2), + _ => Err(format!("Invalid Profile {}", value)), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ObuHeader { + pub obu_type: ObuType, + pub extension_flag: bool, + pub has_size_field: bool, + pub temporal_id: u32, + pub spatial_id: u32, +} + +impl ObuHeader { + /// Length in bytes + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + if self.extension_flag { + 2 + } else { + 1 + } + } +} + +/// Contains the OBU header and a reference to its data. The OBU itself hasn't been parsed yet. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Obu<'a> { + /// The OBU header. + pub header: ObuHeader, + /// Amount of bytes from the input consumed to parse this OBU. + pub bytes_used: usize, + /// The slice backing the OBU. + data: Cow<'a, [u8]>, +} + +impl<'a> AsRef<[u8]> for Obu<'a> { + fn as_ref(&self) -> &[u8] { + self.data.as_ref() + } +} + +/// A fully parsed OBU, with additional data when relevant. +pub enum ParsedObu<'a> { + Reserved, + SequenceHeader(Rc), + TemporalDelimiter, + FrameHeader(FrameHeaderObu), + TileGroup(TileGroupObu<'a>), + Metadata, + Frame(FrameObu<'a>), + RedundantFrameHeader, + TileList, + Reserved2, + Reserved3, + Reserved4, + Reserved5, + Reserved6, + Reserved7, + Padding, +} + +impl<'a> ParsedObu<'a> { + pub fn obu_type(&self) -> ObuType { + match self { + ParsedObu::Reserved => ObuType::Reserved, + ParsedObu::SequenceHeader(_) => ObuType::SequenceHeader, + ParsedObu::TemporalDelimiter => ObuType::TemporalDelimiter, + ParsedObu::FrameHeader(_) => ObuType::FrameHeader, + ParsedObu::TileGroup(_) => ObuType::TileGroup, + ParsedObu::Metadata => ObuType::Metadata, + ParsedObu::Frame(_) => ObuType::Frame, + ParsedObu::RedundantFrameHeader => ObuType::RedundantFrameHeader, + ParsedObu::TileList => ObuType::TileList, + ParsedObu::Reserved2 => ObuType::Reserved2, + ParsedObu::Reserved3 => ObuType::Reserved3, + ParsedObu::Reserved4 => ObuType::Reserved4, + ParsedObu::Reserved5 => ObuType::Reserved5, + ParsedObu::Reserved6 => ObuType::Reserved6, + ParsedObu::Reserved7 => ObuType::Reserved7, + ParsedObu::Padding => ObuType::Padding, + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Tile { + /// Same as TileOffset in the specification. + pub tile_offset: u32, + /// Same as TileSize in the specification. + pub tile_size: u32, + /// Same as TileRow in the specification. + pub tile_row: u32, + /// Same as TileCol in the specification. + pub tile_col: u32, + // Same as MiRowStart in the specification. + pub mi_row_start: u32, + // Same as MiRowEnd in the specification. + pub mi_row_end: u32, + // Same as MiColStart in the specification. + pub mi_col_start: u32, + // Same as MiColEnd in the specification. + pub mi_col_end: u32, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct TileGroupObu<'a> { + /// The OBU backing this tile group. + pub obu: Obu<'a>, + /// Specifies whether tg_start and tg_end are present. If tg_start and + /// tg_end are not present, this tile group covers the entire frame. + pub tile_start_and_end_present_flag: bool, + /// Specifies the zero-based index of the first tile in the current tile + /// group. + pub tg_start: u32, + /// Specifies the zero-based index of the last tile in the current tile + /// group. + pub tg_end: u32, + /// Contains the tiles in this tile group. Use `tile_offset`to index into + /// the OBU data. + /// + /// The tiles in the Vec span from tg_start to tg_end. + pub tiles: Vec, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct OperatingPoint { + /// Specifies the level that the coded video sequence conforms to when + /// operating point i is selected. + pub seq_level_idx: u8, + /// Specifies the tier that the coded video sequence conforms to when + /// operating point i is selected. + pub seq_tier: u8, + /// Specifies the value of operating_point_idc for the selected operating + /// point. + pub idc: u16, + /// If set, indicates that there is a decoder model associated with + /// operating point i. If not set, indicates that there is not a decoder + /// model associated with operating point i. + pub decoder_model_present_for_this_op: bool, + /// Specifies the time interval between the arrival of the first bit in the + /// smoothing buffer and the subsequent removal of the data that belongs to + /// the first coded frame for operating point op, measured in units of + /// 1/90000 seconds. The length of decoder_buffer_delay is specified by + /// buffer_delay_length_minus_1 + 1, in bits. + pub decoder_buffer_delay: u32, + /// Specifies, in combination with decoder_buffer_delay\[ op \] syntax + /// element, the first bit arrival time of frames to be decoded to the + /// smoothing buffer. encoder_buffer_delay is measured in units of 1/90000 + /// seconds. + pub encoder_buffer_delay: u32, + /// If set, indicates that the smoothing buffer operates in low-delay mode + /// for operating point op. In low-delay mode late decode times and buffer + /// underflow are both permitted. If not set, indicates that the smoothing + /// buffer operates in strict mode, where buffer underflow is not allowed. + pub low_delay_mode_flag: bool, + /// If set, indicates that initial_display_delay_minus_1 is specified for + /// operating point i. If not set, indicates that + /// initial_display_delay_minus_1 is not specified for operating point i. + pub initial_display_delay_present_for_this_op: bool, + /// Plus 1 specifies, for operating point i, the number of decoded frames + /// that should be present in the buffer pool before the first presentable + /// frame is displayed. This will ensure that all presentable frames in the + /// sequence can be decoded at or before the time that they are scheduled + /// for display. If not signaled then initial_display_delay_minus_1\[ i \] = + /// BUFFER_POOL_MAX_SIZE - 1. + pub initial_display_delay_minus_1: u32, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct TimingInfo { + /// The number of time units of a clock operating at the frequency + /// time_scale Hz that corresponds to one increment of a clock tick counter. + /// A display clock tick, in seconds, is equal to num_units_in_display_tick + /// divided by time_scale: + pub num_units_in_display_tick: u32, + /// The number of time units that pass in one second. + pub time_scale: u32, + /// If set, indicates that pictures should be displayed according to their + /// output order with the number of ticks between two consecutive pictures + /// (without dropping frames) specified by num_ticks_per_picture_minus_1 + + /// 1. If not set, indicates that the interval between two consecutive + /// pictures is not specified. + pub equal_picture_interval: bool, + /// Plus 1 specifies the number of clock ticks corresponding to output time + /// between two consecutive pictures in the output order. + pub num_ticks_per_picture_minus_1: u32, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct DecoderModelInfo { + /// Plus 1 specifies the length of the decoder_buffer_delay and the + /// encoder_buffer_delay syntax elements, in bits. + pub buffer_delay_length_minus_1: u8, + /// The number of time units of a decoding clock operating at the frequency + /// time_scale Hz that corresponds to one increment of a clock tick counter: + pub num_units_in_decoding_tick: u32, + /// Plus 1 specifies the length of the buffer_removal_time syntax element, + /// in bits. + pub buffer_removal_time_length_minus_1: u8, + /// Plus 1 specifies the length of the frame_presentation_time syntax + /// element, in bits. + pub frame_presentation_time_length_minus_1: u32, +} + +/// Defined by the “Color primaries” section of ISO/IEC 23091-4/ITU-T H.273 +/// See 6.4.2 +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum ColorPrimaries { + Bt709 = 1, + #[default] + Unspecified = 2, + Bt470M = 4, + Bt470bg = 5, + Bt601 = 6, + Smpte240 = 7, + GenericFilm = 8, + Bt2020 = 9, + Xyz = 10, + Smpte431 = 11, + Smpte432 = 12, + Ebu3213 = 22, +} + +impl TryFrom for ColorPrimaries { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 1 => Ok(ColorPrimaries::Bt709), + 2 => Ok(ColorPrimaries::Unspecified), + 4 => Ok(ColorPrimaries::Bt470M), + 5 => Ok(ColorPrimaries::Bt470bg), + 6 => Ok(ColorPrimaries::Bt601), + 7 => Ok(ColorPrimaries::Smpte240), + 8 => Ok(ColorPrimaries::GenericFilm), + 9 => Ok(ColorPrimaries::Bt2020), + 10 => Ok(ColorPrimaries::Xyz), + 11 => Ok(ColorPrimaries::Smpte431), + 12 => Ok(ColorPrimaries::Smpte432), + 22 => Ok(ColorPrimaries::Ebu3213), + _ => Err(format!("Invalid ColorPrimaries {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum TransferCharacteristics { + Reserved0 = 0, + Bt709 = 1, + #[default] + Unspecified = 2, + Reserved3 = 3, + Bt470m = 4, + Bt470bg = 5, + Bt601 = 6, + Smpte240 = 7, + Linear = 8, + Log100 = 9, + Log100Sqrt10 = 10, + Iec61966 = 11, + Bt1361 = 12, + Srgb = 13, + Bt202010Bit = 14, + Bt202012Bit = 15, + Smpte2084 = 16, + Smpte428 = 17, + Hlg = 18, +} + +impl TryFrom for TransferCharacteristics { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(TransferCharacteristics::Reserved0), + 1 => Ok(TransferCharacteristics::Bt709), + 2 => Ok(TransferCharacteristics::Unspecified), + 3 => Ok(TransferCharacteristics::Reserved3), + 4 => Ok(TransferCharacteristics::Bt470m), + 5 => Ok(TransferCharacteristics::Bt470bg), + 6 => Ok(TransferCharacteristics::Bt601), + 7 => Ok(TransferCharacteristics::Smpte240), + 8 => Ok(TransferCharacteristics::Linear), + 9 => Ok(TransferCharacteristics::Log100), + 10 => Ok(TransferCharacteristics::Log100Sqrt10), + 11 => Ok(TransferCharacteristics::Iec61966), + 12 => Ok(TransferCharacteristics::Bt1361), + 13 => Ok(TransferCharacteristics::Srgb), + 14 => Ok(TransferCharacteristics::Bt202010Bit), + 15 => Ok(TransferCharacteristics::Bt202012Bit), + 16 => Ok(TransferCharacteristics::Smpte2084), + 17 => Ok(TransferCharacteristics::Smpte428), + 18 => Ok(TransferCharacteristics::Hlg), + _ => Err(format!("Invalid TransferCharacteristics {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum BitDepth { + #[default] + Depth8 = 0, + Depth10 = 1, + Depth12 = 2, +} + +impl TryFrom for BitDepth { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(BitDepth::Depth8), + 1 => Ok(BitDepth::Depth10), + 2 => Ok(BitDepth::Depth12), + _ => Err(format!("Invalid BitDepth {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum MatrixCoefficients { + Identity = 0, + Bt709 = 1, + #[default] + Unspecified = 2, + Reserved3 = 3, + Fcc = 4, + Bt470bg = 5, + Bt601 = 6, + Smpte240 = 7, + Ycgco = 8, + Bt2020Ncl = 9, + Bt2020Cl = 10, + Smpte2085 = 11, + ChromaDerivedNcl = 12, + ChromaDerivedCl = 13, + Ictcp = 14, +} + +impl TryFrom for MatrixCoefficients { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(MatrixCoefficients::Identity), + 1 => Ok(MatrixCoefficients::Bt709), + 2 => Ok(MatrixCoefficients::Unspecified), + 3 => Ok(MatrixCoefficients::Reserved3), + 4 => Ok(MatrixCoefficients::Fcc), + 5 => Ok(MatrixCoefficients::Bt470bg), + 6 => Ok(MatrixCoefficients::Bt601), + 7 => Ok(MatrixCoefficients::Smpte240), + 8 => Ok(MatrixCoefficients::Ycgco), + 9 => Ok(MatrixCoefficients::Bt2020Ncl), + 10 => Ok(MatrixCoefficients::Bt2020Cl), + 11 => Ok(MatrixCoefficients::Smpte2085), + 12 => Ok(MatrixCoefficients::ChromaDerivedNcl), + 13 => Ok(MatrixCoefficients::ChromaDerivedCl), + 14 => Ok(MatrixCoefficients::Ictcp), + _ => Err(format!("Invalid MatrixCoefficients {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum ChromaSamplePosition { + #[default] + Unknown = 0, + Vertical = 1, + Colocated = 2, + Reserved = 3, +} + +impl TryFrom for ChromaSamplePosition { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ChromaSamplePosition::Unknown), + 1 => Ok(ChromaSamplePosition::Vertical), + 2 => Ok(ChromaSamplePosition::Colocated), + 3 => Ok(ChromaSamplePosition::Reserved), + _ => Err(format!("Invalid ChromaSamplePosition {}", value)), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ColorConfig { + /// Syntax elements which, together with seq_profile, determine the bit + /// depth. + pub high_bitdepth: bool, + /// Syntax elements which, together with seq_profile, determine the bit + /// depth. + pub twelve_bit: bool, + /// If set, indicates that the video does not contain U and V color planes. + /// If not set, indicates that the video contains Y, U, and V color planes. + pub mono_chrome: bool, + /// If set, specifies that color_primaries, transfer_characteristics, and + /// matrix_coefficients are present. If not set, specifies that + /// color_primaries, transfer_characteristics and matrix_coefficients are + /// not present. + pub color_description_present_flag: bool, + /// Defined by the “Color primaries” section of ISO/IEC 23091-4/ITU-T H.273. + pub color_primaries: ColorPrimaries, + /// Defined by the “Transfer characteristics” section of ISO/IEC + /// 23091-4/ITU-T H.273. + pub transfer_characteristics: TransferCharacteristics, + /// Defined by the “Matrix coefficients” section of ISO/IEC 23091-4/ITU-T + /// H.273. + pub matrix_coefficients: MatrixCoefficients, + /// Binary value that is associated with the VideoFullRangeFlag variable + /// specified in ISO/IEC 23091-4/ITU- T H.273. color range equal to 0 shall + /// be referred to as the studio swing representation and color range equal + /// to 1 shall be referred to as the full swing representation for all + /// intents relating to this specification. + pub color_range: bool, + /// Specify the chroma subsampling format + pub subsampling_x: bool, + /// Specify the chroma subsampling format + pub subsampling_y: bool, + /// Specifies the sample position for subsampled streams + pub chroma_sample_position: ChromaSamplePosition, + /// If set, indicates that the U and V planes may have separate delta + /// quantizer values. If not set, indicates that the U and V planes will + /// share the same delta quantizer value. + pub separate_uv_delta_q: bool, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SequenceHeaderObu { + /// The OBU header from the OBU that generated this sequence. + pub obu_header: ObuHeader, + /// Specifies the features that can be used in the coded video sequence. + pub seq_profile: Profile, + /// If set, specifies that the coded video sequence contains only one coded + /// frame. If not set, specifies that the coded video sequence contains one + /// or more coded frames. + pub still_picture: bool, + /// Specifies that the syntax elements not needed by a still picture are + /// omitted. + pub reduced_still_picture_header: bool, + /// Specifies the number of bits minus 1 used for transmitting the frame + /// width syntax elements. + pub frame_width_bits_minus_1: u8, + /// Specifies the number of bits minus 1 used for transmitting the frame + /// height syntax elements. + pub frame_height_bits_minus_1: u8, + /// Specifies the maximum frame width minus 1 for the frames represented by + /// this sequence header. + pub max_frame_width_minus_1: u16, + /// Specifies the maximum frame height minus 1 for the frames represented by + /// this sequence header. + pub max_frame_height_minus_1: u16, + /// Specifies whether frame id numbers are present in the coded video + /// sequence. + pub frame_id_numbers_present_flag: bool, + /// Specifies the number of bits minus 2 used to encode delta_frame_id + /// syntax elements. + pub delta_frame_id_length_minus_2: u32, + /// Used to calculate the number of bits used to encode the frame_id syntax + /// element. + pub additional_frame_id_length_minus_1: u32, + /// When set, indicates that superblocks contain 128x128 luma samples. When + /// not set, it indicates that superblocks contain 64x64 luma samples. (The + /// number of contained chroma samples depends on subsampling_x and + /// subsampling_y.) + pub use_128x128_superblock: bool, + /// When set, specifies that the use_filter_intra syntax element may be + /// present. When not set, specifies that the use_filter_intra syntax + /// element will not be present. + pub enable_filter_intra: bool, + /// Specifies whether the intra edge filtering process should be enabled. + pub enable_intra_edge_filter: bool, + /// When set, specifies that the mode info for inter blocks may contain the + /// syntax element interintra. If not set, specifies that the syntax element + /// interintra will not be present. + pub enable_interintra_compound: bool, + /// When set, specifies that the mode info for inter blocks may contain the + /// syntax element compound_type. When not set, specifies that the syntax + /// element compound_type will not be present. + pub enable_masked_compound: bool, + /// When set, indicates that the allow_warped_motion syntax element may be + /// present. When not set, indicates that the allow_warped_motion syntax + /// element will not be present. + pub enable_warped_motion: bool, + /// When set, indicates that tools based on the values of order hints may be + /// used. When not set, indicates that tools based on order hints are + /// disabled. + pub enable_order_hint: bool, + /// When set, indicates that the inter prediction filter type may be + /// specified independently in the horizontal and vertical directions. If + /// the flag is not set, only one filter type may be specified, which is + /// then used in both directions. + pub enable_dual_filter: bool, + /// If set, indicates that the distance weights process may be used for + /// inter prediction. + pub enable_jnt_comp: bool, + /// If set, indicates that the use_ref_frame_mvs syntax element may be + /// present. If not set, indicates that the use_ref_frame_mvs syntax element + /// will not be present. + pub enable_ref_frame_mvs: bool, + /// If not set, indicates that the seq_force_screen_content_tools syntax + /// element will be present. If set, indicates that + /// seq_force_screen_content_tools should be set equal to + /// SELECT_SCREEN_CONTENT_TOOLS. + pub seq_choose_screen_content_tools: bool, + /// Equal to SELECT_SCREEN_CONTENT_TOOLS indicates that the + /// allow_screen_content_tools syntax element will be present in the frame + /// header. Otherwise, seq_force_screen_content_tools contains the value for + /// allow_screen_content_tools. + pub seq_force_screen_content_tools: u32, + /// If not set, indicates that the seq_force_integer_mv syntax element will + /// be present. If set, indicates that seq_force_integer_mv should be set + /// equal to SELECT_INTEGER_MV. + pub seq_choose_integer_mv: bool, + /// Equal to SELECT_INTEGER_MV indicates that the force_integer_mv syntax + /// element will be present in the frame header (providing + /// allow_screen_content_tools is equal to 1). Otherwise, + /// seq_force_integer_mv contains the value for force_integer_mv. + pub seq_force_integer_mv: u32, + /// Used to compute OrderHintBits. + pub order_hint_bits_minus_1: i32, + /// Specifies the number of bits used for the order_hint syntax element. + pub order_hint_bits: i32, + /// If set, specifies that the use_superres syntax element will be present + /// in the uncompressed header. If not set, specifies that the use_superres + /// syntax element will not be present (instead use_superres will be set to + /// 0 in the uncompressed header without being read). + pub enable_superres: bool, + /// If set, specifies that cdef filtering may be enabled. If not set, + /// specifies that cdef filtering is disabled. + pub enable_cdef: bool, + /// If set, specifies that loop restoration filtering may be enabled. If + /// not set, specifies that loop restoration filtering is disabled. + pub enable_restoration: bool, + /// Specifies whether film grain parameters are present in the coded video + /// sequence. + pub film_grain_params_present: bool, + /// Indicates the number of operating points minus 1 present in the coded + /// video sequence. An operating point specifies which spatial and temporal + /// layers should be decoded. + pub operating_points_cnt_minus_1: u32, + /// The set of operating points. + pub operating_points: [OperatingPoint; MAX_NUM_OPERATING_POINTS], + /// Specifies whether decoder model information is present in the coded + /// video sequence. + pub decoder_model_info_present_flag: bool, + /// The decoder model info. + pub decoder_model_info: DecoderModelInfo, + /// Specifies whether initial display delay information is present in the + /// coded video sequence. + pub initial_display_delay_present_flag: bool, + /// Specifies whether timing info is present in the coded video sequence. + pub timing_info_present_flag: bool, + /// The timing info. + pub timing_info: TimingInfo, + /// The color config. + pub color_config: ColorConfig, + + /* CamelCase variables in the specification */ + pub bit_depth: BitDepth, + pub num_planes: u32, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct StreamInfo { + pub seq_header: Rc, + pub render_width: u32, + pub render_height: u32, +} + +/// A TemporalDelimiterOBU +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct TemporalDelimiterObu { + pub obu_header: ObuHeader, +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum InterpolationFilter { + #[default] + EightTap = 0, + EightTapSmooth = 1, + EightTapSharp = 2, + Bilinear = 3, + Switchable = 4, +} + +impl TryFrom for InterpolationFilter { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(InterpolationFilter::EightTap), + 1 => Ok(InterpolationFilter::EightTapSmooth), + 2 => Ok(InterpolationFilter::EightTapSharp), + 3 => Ok(InterpolationFilter::Bilinear), + 4 => Ok(InterpolationFilter::Switchable), + _ => Err(format!("Invalid InterpolationFilter {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum TxModes { + #[default] + Only4x4 = 0, + Largest = 1, + Select = 2, +} + +impl TryFrom for TxModes { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(TxModes::Only4x4), + 1 => Ok(TxModes::Largest), + 2 => Ok(TxModes::Select), + _ => Err(format!("Invalid TxModes {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum FrameRestorationType { + #[default] + None = 0, + Wiener = 1, + Sgrproj = 2, + Switchable = 3, +} + +impl TryFrom for FrameRestorationType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(FrameRestorationType::None), + 1 => Ok(FrameRestorationType::Wiener), + 2 => Ok(FrameRestorationType::Sgrproj), + 3 => Ok(FrameRestorationType::Switchable), + _ => Err(format!("Invalid FrameRestorationType {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum ReferenceFrameType { + #[default] + Intra = 0, + Last = 1, + Last2 = 2, + Last3 = 3, + Golden = 4, + BwdRef = 5, + AltRef2 = 6, + AltRef = 7, +} + +impl TryFrom for ReferenceFrameType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ReferenceFrameType::Intra), + 1 => Ok(ReferenceFrameType::Last), + 2 => Ok(ReferenceFrameType::Last2), + 3 => Ok(ReferenceFrameType::Last3), + 4 => Ok(ReferenceFrameType::Golden), + 5 => Ok(ReferenceFrameType::BwdRef), + 6 => Ok(ReferenceFrameType::AltRef2), + 7 => Ok(ReferenceFrameType::AltRef), + _ => Err(format!("Invalid ReferenceFrameType {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum WarpModelType { + #[default] + Identity = 0, + Translation = 1, + RotZoom = 2, + Affine = 3, +} + +impl TryFrom for WarpModelType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(WarpModelType::Identity), + 1 => Ok(WarpModelType::Translation), + 2 => Ok(WarpModelType::RotZoom), + 3 => Ok(WarpModelType::Affine), + _ => Err(format!("Invalid WarpModelType {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum FrameType { + #[default] + KeyFrame = 0, + InterFrame = 1, + IntraOnlyFrame = 2, + SwitchFrame = 3, +} + +impl TryFrom for FrameType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(FrameType::KeyFrame), + 1 => Ok(FrameType::InterFrame), + 2 => Ok(FrameType::IntraOnlyFrame), + 3 => Ok(FrameType::SwitchFrame), + _ => Err(format!("Invalid FrameType {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum TxMode { + #[default] + Only4x4 = 0, + Largest = 1, + Select = 2, +} + +impl TryFrom for TxMode { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(TxMode::Only4x4), + 1 => Ok(TxMode::Largest), + 2 => Ok(TxMode::Select), + _ => Err(format!("Invalid TxMode {}", value)), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct FrameObu<'a> { + pub header: FrameHeaderObu, + pub tile_group: TileGroupObu<'a>, +} + +/// A FrameHeaderOBU +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct FrameHeaderObu { + /// The original OBU header. This may be from a FrameOBU or a FrameHeaderOBU + /// directly. + pub obu_header: ObuHeader, + /// If set, indicates the frame indexed by frame_to_show_map_idx is to be + /// output; If not set, indicates that further processing is required. + pub show_existing_frame: bool, + /// Specifies the frame to be output. It is only available if + /// show_existing_frame is set. + pub frame_to_show_map_idx: u8, + /// Specifies the length of the frame_presentation_time syntax element, in + /// bits. + pub frame_presentation_time: u32, + /// Provides the frame id number for the frame to output. + pub display_frame_id: u32, + /// Specifies the type of the frame + pub frame_type: FrameType, + /// If set, specifies that this frame should be immediately output once + /// decoded. If not set specifies that this frame should not be + /// immediately output. (It may be output later if a later uncompressed + /// header uses show_existing_frame is set). + pub show_frame: bool, + /// When set, specifies that the frame may be output using the + /// show_existing_frame mechanism. When not set, specifies that this frame + /// will not be output using the show_existing_frame mechanism. + pub showable_frame: bool, + /// If set, indicates that error resilient mode is enabled; + /// error_resilient_mode equal to 0 indicates that error resilient mode is + /// disabled. + pub error_resilient_mode: bool, + /// Specifies whether the CDF update in the symbol decoding process should + /// be disabled. + pub disable_cdf_update: bool, + /// When set, indicates that intra blocks may use palette encoding; When not + /// set, indicates that palette encoding is never used. + pub allow_screen_content_tools: u32, + /// If set, specifies that motion vectors will always be integers. If not + /// set, specifies that motion vectors can contain fractional bits. + pub force_integer_mv: u32, + /// Specifies the frame id number for the current frame. Frame id numbers + /// are additional information that do not affect the decoding process, but + /// provide decoders with a way of detecting missing reference frames so + /// that appropriate action can be taken. + pub current_frame_id: u32, + /// If not set, specifies that the frame size is equal to the size in the + /// sequence header. If set, specifies that the frame size will either be + /// specified as the size of one of the reference frames, or computed from + /// the frame_width_minus_1 and frame_height_minus_1 syntax elements. + pub frame_size_override_flag: bool, + /// Specifies OrderHintBits least significant bits of the expected output + /// order for this frame. + pub order_hint: u32, + /// Specifies which reference frame contains the CDF values and other state + /// that should be loaded at the start of the frame. + pub primary_ref_frame: u32, + /// If set, specifies that buffer_removal_time is present. If not set, + /// specifies that buffer_removal_time is not present. + pub buffer_removal_time_present_flag: bool, + /// Specifies the frame removal time in units of DecCT clock ticks counted + /// from the removal time of the last random access point for operating + /// point opNum. buffer_removal_time is signaled as a fixed length unsigned + /// integer with a length in bits given by + /// buffer_removal_time_length_minus_1 + 1. + pub buffer_removal_time: Vec, + /// Contains a bitmask that specifies which reference frame slots will be + /// updated with the current frame after it is decoded. + pub refresh_frame_flags: u32, + /// Specifies the expected output order hint for each reference frame. + pub ref_order_hint: [u32; NUM_REF_FRAMES], + /// If set, indicates that intra block copy may be used in this frame. If + /// not set indicates that intra block copy is not allowed in this frame. + pub allow_intrabc: bool, + /// If set, indicates that only two reference frames are explicitly + /// signaled. If not set, indicates that all reference frames are explicitly + /// signaled. + pub frame_refs_short_signaling: bool, + /// Specifies the reference frame to use for LAST_FRAME. + pub last_frame_idx: u8, + /// Specifies the reference frame to use for GOLDEN_FRAME. + pub gold_frame_idx: u8, + /// Specifies which reference frames are used by inter frames + pub ref_frame_idx: [u8; REFS_PER_FRAME], + /// If not set, specifies that motion vectors are specified to quarter pel + /// precision; If set, specifies that motion vectors are specified to eighth + /// pel precision. + pub allow_high_precision_mv: bool, + /// If not set, specifies that only the SIMPLE motion mode will be used. + pub is_motion_mode_switchable: bool, + /// If set, specifies that motion vector information from a previous frame + /// can be used when decoding the current frame. If not set, specifies that + /// this information will not be used. + pub use_ref_frame_mvs: bool, + /// If set, indicates that the end of frame CDF update is disabled; If not + /// set, indicates that the end of frame CDF update is enabled. + pub disable_frame_end_update_cdf: bool, + /// If set, indicates that the syntax element motion_mode may be present. + /// If not set, indicates that the syntax element motion_mode will not be + /// present + pub allow_warped_motion: bool, + /// If set, specifies that the frame is restricted to a reduced subset of + /// the full set of transform types. + pub reduced_tx_set: bool, + /// If not set, means that the render width and height are inferred from the + /// frame width and height. If set, means that the render width and height + /// are explicitly coded. + pub render_and_frame_size_different: bool, + /// If not set, indicates that no upscaling is needed. If set, indicates + /// that upscaling is needed. + pub use_superres: bool, + /// If set indicates that the filter selection is signaled at the block + /// level; If not set, indicates that the filter selection is signaled at + /// the frame level. + pub is_filter_switchable: bool, + /// The interpolation filter parameters. + pub interpolation_filter: InterpolationFilter, + /// The loop filter parameters. + pub loop_filter_params: LoopFilterParams, + /// The quantization parameters. + pub quantization_params: QuantizationParams, + /// The segmentation parameters. + pub segmentation_params: SegmentationParams, + /// The tile info. + pub tile_info: TileInfo, + /// The CDEF parameters. + pub cdef_params: CdefParams, + /// The loop restoration parameters. + pub loop_restoration_params: LoopRestorationParams, + /// Used to compute TxMode. + pub tx_mode_select: u32, + /// If set specifies that the syntax element skip_mode will be present. If + /// not set, specifies that skip_mode will not be used for this frame. + pub skip_mode_present: bool, + /// If set, specifies that the mode info for inter blocks contains the + /// syntax element comp_mode that indicates whether to use single or + /// compound reference prediction. If not set, specifies that all inter + /// blocks will use single prediction. + pub reference_select: bool, + /// The global motion parameters. + pub global_motion_params: GlobalMotionParams, + /// The film grain parameters. + pub film_grain_params: FilmGrainParams, + + /* CamelCase variables */ + pub superres_denom: u32, + pub frame_is_intra: bool, + pub order_hints: [u32; NUM_REF_FRAMES], + pub ref_frame_sign_bias: [bool; NUM_REF_FRAMES], + pub coded_lossless: bool, + pub all_lossless: bool, + pub lossless_array: [bool; MAX_SEGMENTS], + pub seg_qm_level: [[u32; MAX_SEGMENTS]; 3], + pub upscaled_width: u32, + pub frame_width: u32, + pub frame_height: u32, + pub render_width: u32, + pub render_height: u32, + pub tx_mode: TxMode, + pub skip_mode_frame: [u32; 2], + pub mi_cols: u32, + pub mi_rows: u32, + pub header_bytes: usize, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct LoopFilterParams { + /// An array containing loop filter strength values. Different loop filter + /// strength values from the array are used depending on the image plane + /// being filtered, and the edge direction (vertical or horizontal) being + /// filtered. + pub loop_filter_level: [u8; 4], + /// Indicates the sharpness level. The loop_filter_level and + /// loop_filter_sharpness together determine when a block edge is filtered, + /// and by how much the filtering can change the sample values. + pub loop_filter_sharpness: u8, + /// If set, means that the filter level depends on the mode and reference + /// frame used to predict a block. If not set, means that the filter level + /// does not depend on the mode and reference frame. + pub loop_filter_delta_enabled: bool, + /// If set, means that additional syntax elements are present that specify + /// which mode and reference frame deltas are to be updated. + /// loop_filter_delta_update equal to 0 means that these syntax elements are + /// not present. + pub loop_filter_delta_update: bool, + /// Contains the adjustment needed for the filter level based on the chosen + /// reference frame. If this syntax element is not present, it maintains + /// its previous value. + pub loop_filter_ref_deltas: [i8; TOTAL_REFS_PER_FRAME], + /// Contains the adjustment needed for the filter level based on the chosen + /// mode. If this syntax element is not present in the, it maintains its + /// previous value. + pub loop_filter_mode_deltas: [i8; 2], + /// Specifies whether loop filter delta values are present. + pub delta_lf_present: bool, + /// Specifies the left shift which should be applied to decoded loop filter + /// delta values. + pub delta_lf_res: u8, + /// If set, specifies that separate loop filter deltas are sent for + /// horizontal luma edges, vertical luma edges, the U edges, and the V + /// edges. If not set, specifies that the same loop filter delta is used for + /// all edges. + pub delta_lf_multi: bool, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct QuantizationParams { + /// Indicates the base frame qindex. This is used for Y AC coefficients and + /// as the base value for the other quantizers. + pub base_q_idx: u32, + /// Indicates the base frame qindex. This is used for Y AC coefficients and + /// as the base value for the other quantizers. + pub diff_uv_delta: bool, + /// Specifies that the quantizer matrix will be used to compute quantizers. + pub using_qmatrix: bool, + /// Specifies the level in the quantizer matrix that should be used for luma + /// plane decoding. + pub qm_y: u32, + /// Specifies the level in the quantizer matrix that should be used for + /// chroma U plane decoding. + pub qm_u: u32, + /// Specifies the level in the quantizer matrix that should be used for + /// chroma V plane decoding. + pub qm_v: u32, + /// Specifies whether quantizer index delta values are present. + pub delta_q_present: bool, + /// Specifies the left shift which should be applied to decoded quantizer + /// index delta values. + pub delta_q_res: u32, + /// Same as DeltaQYDc + pub delta_q_y_dc: i32, + /// Same as DeltaQUDc + pub delta_q_u_dc: i32, + /// Same as DeltaQUAc + pub delta_q_u_ac: i32, + /// Same as DeltaQVDc + pub delta_q_v_dc: i32, + /// Same as DeltaQVAc + pub delta_q_v_ac: i32, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SegmentationParams { + /// If set, indicates that this frame makes use of the segmentation tool; If + /// not set, indicates that the frame does not use segmentation. + pub segmentation_enabled: bool, + /// If set, indicates that the segmentation map are updated during the + /// decoding of this frame. If not set, means that the segmentation map from + /// the previous frame is used. + pub segmentation_update_map: bool, + /// If set, indicates that the updates to the segmentation map are coded + /// relative to the existing segmentation map. If not set, indicates that + /// the new segmentation map is coded without reference to the existing + /// segmentation map. + pub segmentation_temporal_update: bool, + /// If set, indicates that new parameters are about to be specified for each + /// segment. If not set, indicates that the segmentation parameters should + /// keep their existing values. + pub segmentation_update_data: bool, + /// If not set, indicates that the corresponding feature is unused and has + /// value equal to 0. If set, indicates that the feature value is coded. + pub feature_enabled: [[bool; SEG_LVL_MAX]; MAX_SEGMENTS], + /// Specifies the feature data for a segment feature. + pub feature_data: [[i16; SEG_LVL_MAX]; MAX_SEGMENTS], + /// Same as SegIdPreSkip + pub seg_id_pre_skip: bool, + /// Same as LastActiveSegId + pub last_active_seg_id: u8, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TileInfo { + /// If set, means that the tiles are uniformly spaced across the frame. (In + /// other words, all tiles are the same size except for the ones at the + /// right and bottom edge which can be smaller.) If not set, means that the + /// tile sizes are coded. + pub uniform_tile_spacing_flag: bool, + /// Used to compute TileColsLog2. + pub increment_tile_rows_log2: u32, + /// Specifies the width of a tile minus 1 in units of superblocks. + pub width_in_sbs_minus_1: [u32; MAX_TILE_COLS], + /// Specifies the height of a tile minus 1 in units of superblocks. + pub height_in_sbs_minus_1: [u32; MAX_TILE_ROWS], + /// Specifies which tile to use for the CDF update + pub context_update_tile_id: u32, + /// An array specifying the start column (in units of 4x4 luma samples) for + /// each tile across the image. + pub mi_col_starts: [u32; MAX_TILE_COLS + 1], + /// An array specifying the start row (in units of 4x4 luma samples) for + /// each tile down the image. + pub mi_row_starts: [u32; MAX_TILE_ROWS + 1], + /// Specifies the base 2 logarithm of the desired number of tiles down the + /// frame. + pub tile_cols_log2: u32, + /// Specifies the number of tiles across the frame. + pub tile_cols: u32, + /// Specifies the base 2 logarithm of the desired number of tiles down the + /// frame. + pub tile_rows_log2: u32, + /// Secifies the number of tiles down the frame + pub tile_rows: u32, + /// Specifies the number of bytes needed to code each tile size. + pub tile_size_bytes: u32, +} + +impl Default for TileInfo { + fn default() -> Self { + Self { + uniform_tile_spacing_flag: Default::default(), + increment_tile_rows_log2: Default::default(), + width_in_sbs_minus_1: [0; MAX_TILE_COLS], + height_in_sbs_minus_1: [0; MAX_TILE_ROWS], + context_update_tile_id: Default::default(), + mi_col_starts: [0; MAX_TILE_COLS + 1], + mi_row_starts: [0; MAX_TILE_ROWS + 1], + tile_cols_log2: Default::default(), + tile_cols: Default::default(), + tile_rows_log2: Default::default(), + tile_rows: Default::default(), + tile_size_bytes: Default::default(), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct CdefParams { + /// Controls the amount of damping in the deringing filter. + pub cdef_damping: u32, + /// Specifies the number of bits needed to specify which CDEF filter to + /// apply. + pub cdef_bits: u32, + /// Specify the strength of the primary filter. + pub cdef_y_pri_strength: [u32; CDEF_MAX], + /// Specify the strength of the secondary filter. + pub cdef_y_sec_strength: [u32; CDEF_MAX], + /// Specify the strength of the primary filter. + pub cdef_uv_pri_strength: [u32; CDEF_MAX], + /// Specify the strength of the secondary filter. + pub cdef_uv_sec_strength: [u32; CDEF_MAX], +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct LoopRestorationParams { + /// Specifies if the luma restoration size should be halved. + pub lr_unit_shift: u8, + /// Only present for 4:2:0 formats and specifies if the chroma size should + /// be half the luma size. + pub lr_uv_shift: u8, + /// Same as FrameRestorationType in the specification. + pub frame_restoration_type: [FrameRestorationType; MAX_NUM_PLANES], + /// Same as LoopRestorationSize in the specification. + pub loop_restoration_size: [u16; MAX_NUM_PLANES], + /// Same as UsesLr in the specification. + pub uses_lr: bool, + /// Same as UsesChromaLr in the specification. + pub uses_chroma_lr: bool, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct GlobalMotionParams { + /// Specifies whether global motion parameters are present for a particular + /// reference frame. + pub is_global: [bool; NUM_REF_FRAMES], + /// Specifies whether a particular reference frame uses rotation and zoom + /// global motion. + pub is_rot_zoom: [bool; NUM_REF_FRAMES], + /// Specifies whether a particular reference frame uses translation global + /// motion. + pub is_translation: [bool; NUM_REF_FRAMES], + /// gm_params\[ ref \]\[ j \] is set equal to SavedGmParams\[ + /// frame_to_show_map_idx \]\[ ref \]\[ j \] for ref = LAST_FRAME..ALTREF_FRAME, + /// for j = 0..5. + pub gm_params: [[i32; 6]; NUM_REF_FRAMES], + /// Whether the parameters are valid (see warpValid and section 7.11.3.6) + pub warp_valid: [bool; NUM_REF_FRAMES], + /// Same as GmType. + pub gm_type: [WarpModelType; NUM_REF_FRAMES], +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct FilmGrainParams { + /// If set, specifies that film grain should be added to this frame. If not + /// set, specifies that film grain should not be added. + pub apply_grain: bool, + /// Specifies the starting value for the pseudo-random numbers used during + /// film grain synthesis. + pub grain_seed: u16, + /// If set means that a new set of parameters should be sent. If not set, + /// means that the previous set of parameters should be used. + pub update_grain: bool, + /// Indicates which reference frame contains the film grain parameters to be + /// used for this frame. + pub film_grain_params_ref_idx: u8, + /// Specifies the number of points for the piece-wise linear scaling + /// function of the luma component. + pub num_y_points: u8, + /// Represents the x (luma value) coordinate for the i-th point of the + /// piecewise linear scaling function for luma component. The values are + /// signaled on the scale of 0..255. (In case of 10 bit video, these values + /// correspond to luma values divided by 4. In case of 12 bit video, these + /// values correspond to luma values divided by 16.) + pub point_y_value: [u8; MAX_NUM_Y_POINTS], + /// Pepresents the scaling (output) value for the i-th point of the + /// piecewise linear scaling function for luma component. + pub point_y_scaling: [u8; MAX_NUM_Y_POINTS], + /// Specifies that the chroma scaling is inferred from the luma scaling. + pub chroma_scaling_from_luma: bool, + /// Specifies the number of points for the piece-wise linear scaling + /// function of the cb component. + pub num_cb_points: u8, + /// Represents the x coordinate for the i-th point of the piece-wise linear + /// scaling function for cb component. The values are signaled on the scale + /// of 0..255. + pub point_cb_value: [u8; MAX_NUM_CB_POINTS], + /// Represents the scaling (output) value for the i-th point of the + /// piecewise linear scaling function for cb component. + pub point_cb_scaling: [u8; MAX_NUM_CB_POINTS], + /// Specifies represents the number of points for the piece-wise linear + /// scaling function of the cr component. + pub num_cr_points: u8, + /// Represents the x coordinate for the i-th point of the piece-wise linear + /// scaling function for cr component. The values are signaled on the scale + /// of 0..255. + pub point_cr_value: [u8; MAX_NUM_CR_POINTS], + /// Represents the scaling (output) value for the i-th point of the + /// piecewise linear scaling function for cr component. + pub point_cr_scaling: [u8; MAX_NUM_CR_POINTS], + /// Represents the shift – 8 applied to the values of the chroma component. + /// The grain_scaling_minus_8 can take values of 0..3 and determines the + /// range and quantization step of the standard deviation of film grain. + pub grain_scaling_minus_8: u8, + /// Specifies the number of auto-regressive coefficients for luma and chroma. + pub ar_coeff_lag: u32, + /// Specifies auto-regressive coefficients used for the Y plane. + pub ar_coeffs_y_plus_128: [u8; MAX_NUM_POS_LUMA], + /// Specifies auto-regressive coefficients used for the U plane. + pub ar_coeffs_cb_plus_128: [u8; MAX_NUM_POS_LUMA], + /// Specifies auto-regressive coefficients used for the V plane. + pub ar_coeffs_cr_plus_128: [u8; MAX_NUM_POS_LUMA], + /// Specifies the range of the auto-regressive coefficients. Values of 0, 1, + /// 2, and 3 correspond to the ranges for auto-regressive coefficients of + /// [-2, 2), [-1, 1), [-0.5, 0.5) and [-0.25, 0.25) respectively. + pub ar_coeff_shift_minus_6: u8, + /// Specifies how much the Gaussian random numbers should be scaled down + /// during the grain synthesis process. + pub grain_scale_shift: u8, + /// Represents a multiplier for the cb component used in derivation of the + /// input index to the cb component scaling function. + pub cb_mult: u8, + /// Represents a multiplier for the average luma component used in + /// derivation of the input index to the cb component scaling function. + pub cb_luma_mult: u8, + /// Represents an offset used in derivation of the input index to the cb + /// component scaling function. + pub cb_offset: u16, + /// Represents a multiplier for the cr component used in derivation of the + /// input index to the cr component scaling function. + pub cr_mult: u8, + /// Represents a multiplier for the average luma component used in + /// derivation of the input index to the cr component scaling function. + pub cr_luma_mult: u8, + /// Represents an offset used in derivation of the input index to the cr + /// component scaling function. + pub cr_offset: u16, + /// If set, indicates that the overlap between film grain blocks shall be + /// applied. If not set, indicates that the overlap between film grain + /// blocks shall not be applied. + pub overlap_flag: bool, + /// If set, indicates that clipping to the restricted (studio) range shall + /// be applied to the sample values after adding the film grain (see the + /// semantics for color_range for an explanation of studio swing). If not + /// set, indicates that clipping to the full range shall be applied to the + /// sample values after adding the film grain. + pub clip_to_restricted_range: bool, +} + +/// Keeps track of the state of the reference frames in the parser. All +/// variables are CamelCase. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +struct ReferenceFrameInfo { + /// An array which is indexed by a reference picture slot number. A value of + /// true in the array signifies that the corresponding reference picture + /// slot is valid for use as a reference picture, while a value of false + /// signifies that the corresponding reference picture slot is not valid for + /// use as a reference picture. + ref_valid: bool, + /// Specifies the frame id for each reference frame. + ref_frame_id: u32, + /// See 7.20 Reference Frame Update Process. + ref_upscaled_width: u32, + /// See 7.20 Reference Frame Update Process. + ref_frame_width: u32, + /// See 7.20 Reference Frame Update Process. + ref_frame_height: u32, + /// See 7.20 Reference Frame Update Process. + ref_render_width: u32, + /// See 7.20 Reference Frame Update Process. + ref_render_height: u32, + /// See 7.20 Reference Frame Update Process. + ref_mi_cols: u32, + /// See 7.20 Reference Frame Update Process. + ref_mi_rows: u32, + /// See 7.20 Reference Frame Update Process. + ref_frame_type: FrameType, + /// See 7.20 Reference Frame Update Process. + ref_subsampling_x: bool, + /// See 7.20 Reference Frame Update Process. + ref_subsampling_y: bool, + /// See 7.20 Reference Frame Update Process. + ref_bit_depth: BitDepth, + /// See 7.20 Reference Frame Update Process. + ref_order_hint: u32, + /// The saved segmentation parameters. + segmentation_params: SegmentationParams, + /// The saved global motion parameters. + global_motion_params: GlobalMotionParams, + /// The saved loop filter parameters. + loop_filter_params: LoopFilterParams, + /// The saved film grain parameters. + film_grain_params: FilmGrainParams, + /// The saved tile info parameters. + tile_info: TileInfo, + display_frame_id: u32, + showable_frame: bool, +} + +#[derive(Clone, Debug, Default)] +pub struct AnnexBState { + pub temporal_unit_size: u32, + pub frame_unit_size: u32, + pub temporal_unit_consumed: u32, + pub frame_unit_consumed: u32, +} + +#[derive(Clone, Debug)] +enum StreamFormat { + LowOverhead, + AnnexB(AnnexBState), +} + +#[derive(Debug)] +pub struct Parser { + stream_format: StreamFormat, + operating_point: u32, + /// Same as SeenFrameHeader in the specification + seen_frame_header: bool, + operating_point_idc: u16, + should_probe_for_annexb: bool, + is_first_frame: bool, + ref_info: [ReferenceFrameInfo; NUM_REF_FRAMES], + + /* CamelCase variables */ + mi_cols: u32, + mi_rows: u32, + prev_frame_id: u32, + current_frame_id: u32, + mi_col_starts: [u32; MAX_TILE_COLS + 1], + mi_row_starts: [u32; MAX_TILE_ROWS + 1], + tile_cols_log2: u32, + tile_cols: u32, + tile_rows_log2: u32, + tile_rows: u32, + tile_size_bytes: u32, + + /// We keep this to implement frame_header_copy() in the specification, and to fill in + /// StreamInfo render_width and render_height. + pub last_frame_header: Option, + /// The last SequenceHeaderObu parsed. + pub sequence_header: Option>, +} + +impl Parser { + /// Probes the input data for the Annex B format. Anything other than + /// Ok(true) refers to data in "low-overhead" format instead, as we are trying to parse + fn annexb_probe(data: &[u8]) -> Result { + let mut r = Reader::new(data); + let mut seen_sequence = false; + let mut seen_frame = false; + + // Try reading the first TU and frame unit size + let temporal_unit_size = r.read_leb128()?; + if temporal_unit_size == 0 { + return Ok(false); + } + + let frame_unit_size = r.read_leb128()?; + if frame_unit_size == 0 || frame_unit_size > temporal_unit_size { + return Ok(false); + } + + let obu_length = r.read_leb128()?; + if obu_length == 0 || obu_length > frame_unit_size { + return Ok(false); + } + + // The first OBU in the first frame_unit of each temporal_unit must + // be a temporal delimiter OBU (and this is the only place temporal + // delimiter OBUs can appear) + let header = Self::parse_obu_header(&mut r.clone())?; + if !matches!(header.obu_type, ObuType::TemporalDelimiter) { + return Ok(false); + } + + // Try identifying a sequence and a frame. + r.0.skip_bits(obu_length as usize * 8)?; + let mut num_bytes_read = 0; + + loop { + let obu_length = r.read_leb128()?; + let mut obu_reader = r.clone(); + + r.0.skip_bits(obu_length as usize * 8)?; + num_bytes_read += obu_length; + + if !seen_sequence { + let header = Self::parse_obu_header(&mut obu_reader)?; + seen_sequence = matches!(header.obu_type, ObuType::SequenceHeader); + } + + if !seen_frame { + let header = Self::parse_obu_header(&mut obu_reader)?; + seen_frame = matches!(header.obu_type, ObuType::Frame | ObuType::FrameHeader); + } + + if seen_sequence && seen_frame { + // OK, enough evidence of Annex B format. + return Ok(true); + } + + if num_bytes_read >= frame_unit_size { + // We read what we've identified as the first frame and yet no + // sequence and no actual frames were found. + return Ok(false); + } + } + } + + fn compute_image_size(&mut self, fh: &mut FrameHeaderObu) { + fh.mi_cols = 2 * ((fh.frame_width + 7) >> 3); + fh.mi_rows = 2 * ((fh.frame_height + 7) >> 3); + self.mi_cols = fh.mi_cols; + self.mi_rows = fh.mi_rows; + } + + // 5.9.8 + fn parse_superres_params( + fh: &mut FrameHeaderObu, + r: &mut Reader, + seq: &SequenceHeaderObu, + ) -> Result<(), String> { + if seq.enable_superres { + fh.use_superres = r.0.read_bit()?; + } else { + fh.use_superres = false; + } + + if fh.use_superres { + fh.superres_denom = + r.0.read_bits::(SUPERRES_DENOM_BITS)? + SUPERRES_DENOM_MIN as u32; + } else { + fh.superres_denom = SUPERRES_NUM as u32; + } + + fh.upscaled_width = fh.frame_width; + fh.frame_width = + (fh.upscaled_width * SUPERRES_NUM as u32 + (fh.superres_denom / 2)) / fh.superres_denom; + + Ok(()) + } + + // 7.8 verbatim. + fn set_frame_refs( + &self, + fh: &mut FrameHeaderObu, + ref_order_hint: &[u32; NUM_REF_FRAMES], + ) -> Result<(), String> { + let seq = self.sequence()?; + let mut ref_frame_idx = [-1i32; REFS_PER_FRAME]; + + ref_frame_idx[0] = fh.last_frame_idx.into(); + ref_frame_idx[ReferenceFrameType::Golden as usize - ReferenceFrameType::Last as usize] = + fh.gold_frame_idx.into(); + + let mut used_frame = [false; NUM_REF_FRAMES]; + used_frame[fh.last_frame_idx as usize] = true; + used_frame[fh.gold_frame_idx as usize] = true; + + let cur_frame_hint = 1 << (seq.order_hint_bits - 1); + let mut shifted_order_hints = [0; NUM_REF_FRAMES]; + for i in 0..NUM_REF_FRAMES { + shifted_order_hints[i] = cur_frame_hint + + helpers::get_relative_dist( + seq.enable_order_hint, + seq.order_hint_bits, + ref_order_hint[i].try_into().unwrap(), + fh.order_hint.try_into().unwrap(), + ); + } + + let mut latest_order_hint = shifted_order_hints[fh.last_frame_idx as usize]; + if latest_order_hint >= cur_frame_hint { + return Err("It is a requirement of bitstream conformance that last_order_hint < cur_frame_hint".into()); + } + + let mut earliest_order_hint = shifted_order_hints[fh.gold_frame_idx as usize]; + if earliest_order_hint >= cur_frame_hint { + return Err("It is a requirement of bitstream conformance that gold_order_hint < cur_frame_hint".into()); + } + + let ref_ = helpers::find_latest_backward( + &shifted_order_hints, + &used_frame, + cur_frame_hint, + &mut latest_order_hint, + ); + + if ref_ >= 0 { + ref_frame_idx + [ReferenceFrameType::AltRef as usize - ReferenceFrameType::Last as usize] = ref_; + used_frame[ref_ as usize] = true; + } + + let ref_ = helpers::find_earliest_backward( + &shifted_order_hints, + &used_frame, + cur_frame_hint, + &mut earliest_order_hint, + ); + + if ref_ >= 0 { + ref_frame_idx + [ReferenceFrameType::BwdRef as usize - ReferenceFrameType::Last as usize] = ref_; + used_frame[ref_ as usize] = true; + } + + let ref_ = helpers::find_earliest_backward( + &shifted_order_hints, + &used_frame, + cur_frame_hint, + &mut earliest_order_hint, + ); + + if ref_ >= 0 { + ref_frame_idx + [ReferenceFrameType::AltRef2 as usize - ReferenceFrameType::Last as usize] = ref_; + used_frame[ref_ as usize] = true; + } + + const REF_FRAME_LIST: [usize; 5] = [ + ReferenceFrameType::Last2 as usize - ReferenceFrameType::Last as usize, + ReferenceFrameType::Last3 as usize - ReferenceFrameType::Last as usize, + ReferenceFrameType::BwdRef as usize - ReferenceFrameType::Last as usize, + ReferenceFrameType::AltRef2 as usize - ReferenceFrameType::Last as usize, + ReferenceFrameType::AltRef as usize - ReferenceFrameType::Last as usize, + ]; + + #[allow(clippy::needless_range_loop)] + for i in 0..REFS_PER_FRAME - 2 { + let ref_frame = REF_FRAME_LIST[i]; + + if ref_frame_idx[ref_frame] < 0 { + let ref_ = helpers::find_latest_forward( + &shifted_order_hints, + &used_frame, + cur_frame_hint, + &mut latest_order_hint, + ); + + if ref_ >= 0 { + ref_frame_idx[ref_frame] = ref_; + used_frame[ref_ as usize] = true; + } + } + } + + let mut ref_ = 0; + earliest_order_hint = shifted_order_hints[0]; + #[allow(clippy::needless_range_loop)] + for i in 1..NUM_REF_FRAMES { + let hint = shifted_order_hints[i]; + if hint < earliest_order_hint { + ref_ = i as u8; + earliest_order_hint = hint; + } + } + + fh.ref_frame_idx + .iter_mut() + .zip(ref_frame_idx.iter().copied()) + .for_each(|(dest, src)| *dest = if src < 0 { ref_ } else { src as u8 }); + + Ok(()) + } + + // 5.9.5. + fn parse_frame_size(&mut self, fh: &mut FrameHeaderObu, r: &mut Reader) -> Result<(), String> { + let seq = self.sequence()?; + if fh.frame_size_override_flag { + let n = seq.frame_width_bits_minus_1 + 1; + fh.frame_width = r.0.read_bits::(n as usize)? + 1; + + let n = seq.frame_height_bits_minus_1 + 1; + fh.frame_height = r.0.read_bits::(n as usize)? + 1; + } else { + fh.frame_width = seq.max_frame_width_minus_1 as u32 + 1; + fh.frame_height = seq.max_frame_height_minus_1 as u32 + 1; + } + + Self::parse_superres_params(fh, r, seq)?; + self.compute_image_size(fh); + + Ok(()) + } + + fn parse_render_size(fh: &mut FrameHeaderObu, r: &mut Reader) -> Result<(), String> { + fh.render_and_frame_size_different = r.0.read_bit()?; + if fh.render_and_frame_size_different { + fh.render_width = r.0.read_bits::(16)? + 1; + fh.render_height = r.0.read_bits::(16)? + 1; + } else { + fh.render_width = fh.upscaled_width; + fh.render_height = fh.frame_height; + } + Ok(()) + } + + fn frame_size_with_refs( + &mut self, + fh: &mut FrameHeaderObu, + r: &mut Reader, + ) -> Result<(), String> { + let mut found_ref = false; + let seq = self.sequence()?; + + for i in 0..REFS_PER_FRAME { + found_ref = r.0.read_bit()?; + + if found_ref { + let rf = &self.ref_info[fh.ref_frame_idx[i] as usize]; + fh.upscaled_width = rf.ref_upscaled_width; + fh.frame_width = fh.upscaled_width; + fh.frame_height = rf.ref_frame_height; + fh.render_width = rf.ref_render_width; + fh.render_height = rf.ref_render_height; + break; + } + } + + if !found_ref { + self.parse_frame_size(fh, r)?; + Self::parse_render_size(fh, r)?; + } else { + Self::parse_superres_params(fh, r, seq)?; + self.compute_image_size(fh); + } + + Ok(()) + } + + /// Skip the padding bits, ensuring that they actually make sense. + fn skip_and_check_trailing_bits(r: &mut Reader, obu: &Obu) -> Result<(), String> { + // We can't have that in parse_obu as per the spec, because the reader + // is not initialized on our design at that point, so move the check to + // inside this function. + if obu.data.len() == 0 + || matches!( + obu.header.obu_type, + ObuType::TileList | ObuType::TileGroup | ObuType::Frame + ) + { + return Ok(()); + } + let num_trailing = obu.as_ref().len() as u64 * 8 - r.0.position(); + r.read_trailing_bits(num_trailing)?; + Ok(()) + } + + fn parse_obu_header(r: &mut Reader) -> Result { + let _obu_forbidden_bit = r.0.read_bit()?; + + let mut header = ObuHeader { + obu_type: ObuType::try_from(r.0.read_bits::(4)?)?, + extension_flag: r.0.read_bit()?, + has_size_field: r.0.read_bit()?, + temporal_id: Default::default(), + spatial_id: Default::default(), + }; + + let obu_reserved_1bit = r.0.read_bit()?; + assert!(!obu_reserved_1bit); // Must be set to zero as per spec. + + if header.extension_flag { + header.temporal_id = r.0.read_bits::(3)?; + header.spatial_id = r.0.read_bits::(2)?; + let _ = r.0.read_bits::(3)?; + } + + Ok(header) + } + + /// Parses one OBU from `data`, which can be in Annex B or low-overhead + /// format. + /// + /// `None` may eventually be returned if the OBU is to be dropped. + pub fn read_obu<'a>(&mut self, data: &'a [u8]) -> Result, String> { + if data.is_empty() { + return Err("Empty data".into()); + } + + let mut reader = Reader::new(data); + + if self.should_probe_for_annexb { + // Try probing for Annex B data. + self.stream_format = if matches!(Self::annexb_probe(data), Ok(true)) { + log::debug!("Parsing an Annex B stream"); + StreamFormat::AnnexB(AnnexBState::default()) + } else { + log::debug!("Parsing a low-overhead stream"); + StreamFormat::LowOverhead + }; + + self.should_probe_for_annexb = false; + } + + let obu_length: usize = if let StreamFormat::AnnexB(annexb_state) = &mut self.stream_format + { + // Read the length to skip to the start of the open_bitstream_unit() + // syntax element. + let obu_length = reader.current_annexb_obu_length(annexb_state)?; + match obu_length { + Some(length) => length, + None => return Ok(ObuAction::Drop(reader.consumed(0))), + } + } else { + 0 + }; + + let start_pos = reader.consumed(0); + + // Both "low-overhead" and Annex B are now at the same point, i.e.: a + // open_bitstream_unit() follows. + let header = Self::parse_obu_header(&mut reader)?; + if matches!(self.stream_format, StreamFormat::LowOverhead) { + assert!(header.has_size_field); + } + + let obu_size: usize = if header.has_size_field { + reader.read_leb128()? as usize + } else { + /* trap any bugs when computing the final length */ + obu_length + .checked_sub(1) + .ok_or::("obu_length must be greater than 0".into())? + .checked_sub(usize::from(header.extension_flag)) + .ok_or::("obu_length too short".into())? + }; + + let consumed = reader.consumed(start_pos); + + if let StreamFormat::AnnexB(annexb_state) = &mut self.stream_format { + annexb_state.temporal_unit_consumed += consumed; + annexb_state.frame_unit_consumed += consumed; + + annexb_state.temporal_unit_consumed += u32::try_from(obu_size).unwrap(); + annexb_state.frame_unit_consumed += u32::try_from(obu_size).unwrap(); + } + + assert!(reader.0.position() % 8 == 0); + let start_offset: usize = (reader.0.position() / 8).try_into().unwrap(); + + log::debug!( + "Identified OBU type {:?}, data size: {}, obu_size: {}", + header.obu_type, + start_offset + obu_size, + obu_size + ); + + if header.obu_type != ObuType::SequenceHeader + && header.obu_type != ObuType::TemporalDelimiter + && self.operating_point_idc != 0 + && header.extension_flag + { + let in_temporal_layer = ((self.operating_point_idc >> header.temporal_id) & 1) != 0; + let in_spatial_layer = ((self.operating_point_idc >> (header.spatial_id + 8)) & 1) != 0; + if !in_temporal_layer || !in_spatial_layer { + log::debug!("Dropping obu as per drop_obu() in the specification",); + return Ok(ObuAction::Drop(reader.consumed(0))); + } + } + + Ok(ObuAction::Process(Obu { + header, + data: Cow::from(&data[start_offset..start_offset + obu_size]), + bytes_used: start_offset + obu_size, + })) + } + + fn parse_color_config(s: &mut SequenceHeaderObu, r: &mut Reader) -> Result<(), String> { + let cc = &mut s.color_config; + + cc.high_bitdepth = r.0.read_bit()?; + if s.seq_profile as u32 == 2 && cc.high_bitdepth { + cc.twelve_bit = r.0.read_bit()?; + if cc.twelve_bit { + s.bit_depth = BitDepth::Depth12; + } else { + s.bit_depth = BitDepth::Depth10; + } + } else if s.seq_profile as u32 <= 2 { + s.bit_depth = if cc.high_bitdepth { BitDepth::Depth10 } else { BitDepth::Depth8 }; + } + + if s.seq_profile as u32 == 1 { + cc.mono_chrome = false; + } else { + cc.mono_chrome = r.0.read_bit()?; + } + + if cc.mono_chrome { + s.num_planes = 1; + } else { + s.num_planes = 3; + } + + cc.color_description_present_flag = r.0.read_bit()?; + if cc.color_description_present_flag { + cc.color_primaries = ColorPrimaries::try_from(r.0.read_bits::(8)?)?; + cc.transfer_characteristics = + TransferCharacteristics::try_from(r.0.read_bits::(8)?)?; + cc.matrix_coefficients = MatrixCoefficients::try_from(r.0.read_bits::(8)?)?; + } else { + cc.color_primaries = ColorPrimaries::Unspecified; + cc.transfer_characteristics = TransferCharacteristics::Unspecified; + cc.matrix_coefficients = MatrixCoefficients::Unspecified; + } + + if cc.mono_chrome { + cc.color_range = r.0.read_bit()?; + cc.subsampling_x = true; + cc.subsampling_y = true; + cc.chroma_sample_position = ChromaSamplePosition::Unknown; + cc.separate_uv_delta_q = false; + return Ok(()); + } else if matches!(cc.color_primaries, ColorPrimaries::Bt709) + && matches!(cc.transfer_characteristics, TransferCharacteristics::Srgb) + && matches!(cc.matrix_coefficients, MatrixCoefficients::Identity) + { + cc.color_range = true; + cc.subsampling_x = false; + cc.subsampling_y = false; + } else { + cc.color_range = r.0.read_bit()?; + if s.seq_profile as u32 == 0 { + cc.subsampling_x = true; + cc.subsampling_y = true; + } else if s.seq_profile as u32 == 1 { + cc.subsampling_x = false; + cc.subsampling_y = false; + } else if matches!(s.bit_depth, BitDepth::Depth12) { + cc.subsampling_x = r.0.read_bit()?; + if cc.subsampling_x { + cc.subsampling_y = r.0.read_bit()?; + } else { + cc.subsampling_y = false; + } + } else { + cc.subsampling_x = true; + cc.subsampling_y = false; + } + + if cc.subsampling_x && cc.subsampling_y { + cc.chroma_sample_position = + ChromaSamplePosition::try_from(r.0.read_bits::(2)?)?; + } + } + + cc.separate_uv_delta_q = r.0.read_bit()?; + + Ok(()) + } + + fn parse_operating_parameters_info( + opi: &mut OperatingPoint, + r: &mut Reader, + buffer_delay_length_minus_1: u8, + ) -> Result<(), String> { + let n = buffer_delay_length_minus_1 + 1; + opi.decoder_buffer_delay = r.0.read_bits::(n as usize)?; + opi.encoder_buffer_delay = r.0.read_bits::(n as usize)?; + opi.low_delay_mode_flag = r.0.read_bit()?; + Ok(()) + } + + fn parse_decoder_model_info(dmi: &mut DecoderModelInfo, r: &mut Reader) -> Result<(), String> { + dmi.buffer_delay_length_minus_1 = r.0.read_bits::(5)? as u8; + dmi.num_units_in_decoding_tick = r.0.read_bits::(32)?; + dmi.buffer_removal_time_length_minus_1 = r.0.read_bits::(5)? as u8; + dmi.frame_presentation_time_length_minus_1 = r.0.read_bits::(5)?; + Ok(()) + } + + fn parse_timing_info(ti: &mut TimingInfo, r: &mut Reader) -> Result<(), String> { + ti.num_units_in_display_tick = r.0.read_bits::(32)?; + ti.time_scale = r.0.read_bits::(32)?; + ti.equal_picture_interval = r.0.read_bit()?; + if ti.equal_picture_interval { + ti.num_ticks_per_picture_minus_1 = r.read_uvlc()?; + } + Ok(()) + } + + /// Selects an operating point. Only call this after the Sequence OBU for + /// which the operating point should apply has been parsed. + pub fn choose_operating_point(&mut self, operating_point: u32) -> Result<(), String> { + if operating_point > self.sequence()?.operating_points_cnt_minus_1 { + return Err(format!( + "Invalid operating point {} (max {})", + operating_point, + self.sequence()?.operating_points_cnt_minus_1 + )); + } + self.operating_point = operating_point; + self.operating_point_idc = self.sequence()?.operating_points[operating_point as usize].idc; + Ok(()) + } + + fn parse_temporal_delimiter_obu(&mut self) -> Result<(), String> { + self.seen_frame_header = false; + Ok(()) + } + + fn parse_sequence_header_obu(&mut self, obu: &Obu) -> Result, String> { + let mut s = SequenceHeaderObu { obu_header: obu.header.clone(), ..Default::default() }; + + let mut r = Reader::new(obu.as_ref()); + let profile = r.0.read_bits::(3)?; + + s.seq_profile = Profile::try_from(profile)?; + s.still_picture = r.0.read_bit()?; + s.reduced_still_picture_header = r.0.read_bit()?; + + if s.reduced_still_picture_header { + /* Default::default() already ensures a lot of this, but lets go verbatim */ + s.timing_info_present_flag = false; + s.decoder_model_info_present_flag = false; + s.initial_display_delay_present_flag = false; + s.operating_points_cnt_minus_1 = 0; + s.operating_points[0].idc = 0; + s.operating_points[0].seq_level_idx = r.0.read_bits::(5)? as u8; + s.operating_points[0].seq_tier = 0; + s.operating_points[0].decoder_model_present_for_this_op = false; + s.operating_points[0].initial_display_delay_present_for_this_op = false; + } else { + s.timing_info_present_flag = r.0.read_bit()?; + if s.timing_info_present_flag { + Self::parse_timing_info(&mut s.timing_info, &mut r)?; + s.decoder_model_info_present_flag = r.0.read_bit()?; + if s.decoder_model_info_present_flag { + Self::parse_decoder_model_info(&mut s.decoder_model_info, &mut r)?; + } + } else { + s.decoder_model_info_present_flag = false; + } + + s.initial_display_delay_present_flag = r.0.read_bit()?; + s.operating_points_cnt_minus_1 = r.0.read_bits::(5)?; + if s.operating_points_cnt_minus_1 > MAX_NUM_OPERATING_POINTS as u32 { + return Err(format!( + "Invalid operating_points_cnt_minus_1 {}", + s.operating_points_cnt_minus_1 + )); + } + + for i in 0..=s.operating_points_cnt_minus_1 as usize { + s.operating_points[i].idc = r.0.read_bits::(12)? as u16; + s.operating_points[i].seq_level_idx = r.0.read_bits::(5)? as u8; + if s.operating_points[i].seq_level_idx > 7 { + s.operating_points[i].seq_tier = r.0.read_bit()? as u8; + } else { + s.operating_points[i].seq_tier = 0; + } + if s.decoder_model_info_present_flag { + s.operating_points[i].decoder_model_present_for_this_op = r.0.read_bit()?; + if s.operating_points[i].decoder_model_present_for_this_op { + let buffer_delay_length_minus_1 = + s.decoder_model_info.buffer_delay_length_minus_1; + Self::parse_operating_parameters_info( + &mut s.operating_points[i], + &mut r, + buffer_delay_length_minus_1, + )?; + } + } else { + s.operating_points[i].decoder_model_present_for_this_op = false; + } + + if s.initial_display_delay_present_flag { + s.operating_points[i].initial_display_delay_present_for_this_op = + r.0.read_bit()?; + if s.operating_points[i].initial_display_delay_present_for_this_op { + s.operating_points[i].initial_display_delay_minus_1 = + r.0.read_bits::(4)?; + } + } + } + } + + s.frame_width_bits_minus_1 = r.0.read_bits::(4)? as u8; + s.frame_height_bits_minus_1 = r.0.read_bits::(4)? as u8; + // frame_width_bits_minus_1 has been read from 4 bits, meaning we can read 16 bits at most. + s.max_frame_width_minus_1 = + r.0.read_bits::(s.frame_width_bits_minus_1 as usize + 1)? as u16; + // frame_height_bits_minus_1 has been read from 4 bits, meaning we can read 16 bits at most. + s.max_frame_height_minus_1 = + r.0.read_bits::(s.frame_height_bits_minus_1 as usize + 1)? as u16; + if s.reduced_still_picture_header { + s.frame_id_numbers_present_flag = false; + } else { + s.frame_id_numbers_present_flag = r.0.read_bit()?; + } + if s.frame_id_numbers_present_flag { + s.delta_frame_id_length_minus_2 = r.0.read_bits::(4)?; + s.additional_frame_id_length_minus_1 = r.0.read_bits::(3)?; + let frame_id_length = + s.additional_frame_id_length_minus_1 + s.delta_frame_id_length_minus_2 + 3; + if frame_id_length > 16 { + return Err(format!("Invalid frame_id_length {}", frame_id_length)); + } + } + + s.use_128x128_superblock = r.0.read_bit()?; + s.enable_filter_intra = r.0.read_bit()?; + s.enable_intra_edge_filter = r.0.read_bit()?; + if s.reduced_still_picture_header { + s.enable_interintra_compound = false; + s.enable_masked_compound = false; + s.enable_warped_motion = false; + s.enable_dual_filter = false; + s.enable_order_hint = false; + s.enable_jnt_comp = false; + s.enable_ref_frame_mvs = false; + s.seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS as _; + s.seq_force_integer_mv = SELECT_INTEGER_MV as _; + s.order_hint_bits = 0; + s.order_hint_bits_minus_1 = -1; + } else { + s.enable_interintra_compound = r.0.read_bit()?; + s.enable_masked_compound = r.0.read_bit()?; + s.enable_warped_motion = r.0.read_bit()?; + s.enable_dual_filter = r.0.read_bit()?; + s.enable_order_hint = r.0.read_bit()?; + if s.enable_order_hint { + s.enable_jnt_comp = r.0.read_bit()?; + s.enable_ref_frame_mvs = r.0.read_bit()?; + } else { + s.enable_jnt_comp = false; + s.enable_ref_frame_mvs = false; + } + s.seq_choose_screen_content_tools = r.0.read_bit()?; + if s.seq_choose_screen_content_tools { + s.seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS as _; + } else { + s.seq_force_screen_content_tools = r.0.read_bit()? as _; + } + if s.seq_force_screen_content_tools > 0 { + s.seq_choose_integer_mv = r.0.read_bit()?; + if s.seq_choose_integer_mv { + s.seq_force_integer_mv = SELECT_INTEGER_MV as _; + } else { + s.seq_force_integer_mv = r.0.read_bit()? as _; + } + } else { + s.seq_force_integer_mv = SELECT_INTEGER_MV as _; + } + + if s.enable_order_hint { + s.order_hint_bits_minus_1 = r.0.read_bits::(3)?.try_into().unwrap(); + s.order_hint_bits = s.order_hint_bits_minus_1 + 1; + } else { + s.order_hint_bits_minus_1 = -1; + s.order_hint_bits = 0; + } + } + + s.enable_superres = r.0.read_bit()?; + s.enable_cdef = r.0.read_bit()?; + s.enable_restoration = r.0.read_bit()?; + + Self::parse_color_config(&mut s, &mut r)?; + + s.film_grain_params_present = r.0.read_bit()?; + + Self::skip_and_check_trailing_bits(&mut r, obu)?; + let rc = Rc::new(s); + self.sequence_header = Some(rc.clone()); + + /* Client is supposed to set the operating point through external means, + * here we just set 0 as default. */ + self.choose_operating_point(0)?; + + Ok(rc) + } + + /// Implements 7.21. Note that 7.20 will use the information from the + /// header, so we must save them now, as they will not be parsed from the + /// bitstream. We also save some internal parser state which will be useful + /// later. + fn load_reference_frame(&self, fh: &mut FrameHeaderObu) -> Result<(), String> { + let rf = &self.ref_info[fh.frame_to_show_map_idx as usize]; + + // Section 6.8.1: It is a requirement of bitstream conformance that a + // sequence header OBU has been received before a frame header OBU. + let seq = self.sequence()?; + + /* at least save the sizes and for both kf and non-kf */ + fh.frame_type = rf.ref_frame_type; + fh.upscaled_width = rf.ref_upscaled_width; + fh.frame_width = rf.ref_frame_width; + fh.frame_height = rf.ref_frame_height; + fh.render_width = rf.ref_render_width; + fh.render_height = rf.ref_render_height; + + /* Save into the frame header */ + if fh.frame_type == FrameType::KeyFrame { + fh.current_frame_id = rf.ref_frame_id; + /* We don't keep track of sequence information at the frame level */ + fh.mi_cols = rf.ref_mi_cols; + fh.mi_rows = rf.ref_mi_rows; + /* The accelerator is keeping track of CDF values, so that is skipped too */ + fh.global_motion_params = rf.global_motion_params.clone(); + + if seq.film_grain_params_present { + fh.film_grain_params = rf.film_grain_params.clone(); + } + fh.loop_filter_params = rf.loop_filter_params.clone(); + fh.segmentation_params = rf.segmentation_params.clone(); + } + + Ok(()) + } + + fn setup_past_independence(fh: &mut FrameHeaderObu) { + fh.segmentation_params.feature_enabled = Default::default(); + fh.segmentation_params.feature_data = Default::default(); + + for i in ReferenceFrameType::Last as usize..ReferenceFrameType::AltRef as usize { + fh.global_motion_params.gm_type[i] = WarpModelType::Identity; + } + + fh.loop_filter_params.loop_filter_delta_enabled = true; + fh.loop_filter_params.loop_filter_ref_deltas = [1, 0, 0, 0, -1, 0, -1, -1]; + fh.loop_filter_params.loop_filter_mode_deltas = Default::default(); + } + + fn parse_tile_info(&mut self, r: &mut Reader, ti: &mut TileInfo) -> Result<(), String> { + let seq = self.sequence()?; + + let sb_cols = if seq.use_128x128_superblock { + (self.mi_cols + 31) >> 5 + } else { + (self.mi_cols + 15) >> 4 + }; + + let sb_rows = if seq.use_128x128_superblock { + (self.mi_rows + 31) >> 5 + } else { + (self.mi_rows + 15) >> 4 + }; + + let sb_shift = if seq.use_128x128_superblock { 5 } else { 4 }; + let sb_size = sb_shift + 2; + + let max_tile_width_sb = MAX_TILE_WIDTH >> sb_size; + let mut max_tile_area_sb = MAX_TILE_AREA >> (2 * sb_size); + + let min_log2_tile_cols = helpers::tile_log2(max_tile_width_sb, sb_cols); + + let max_log2_tile_cols = + helpers::tile_log2(1, std::cmp::min(sb_cols, MAX_TILE_COLS as u32)); + + let max_log2_tile_rows = + helpers::tile_log2(1, std::cmp::min(sb_rows, MAX_TILE_ROWS as u32)); + + let min_log2_tiles = std::cmp::max( + min_log2_tile_cols, + helpers::tile_log2(max_tile_area_sb, sb_rows * sb_cols), + ); + + ti.uniform_tile_spacing_flag = r.0.read_bit()?; + + if ti.uniform_tile_spacing_flag { + self.tile_cols_log2 = min_log2_tile_cols; + while self.tile_cols_log2 < max_log2_tile_cols { + let increment_tile_cols_log_2 = r.0.read_bit()?; + if increment_tile_cols_log_2 { + self.tile_cols_log2 += 1; + } else { + break; + } + } + + let tile_width_sb = (sb_cols + (1 << self.tile_cols_log2) - 1) >> self.tile_cols_log2; + + let mut i = 0; + let mut start_sb = 0; + + while start_sb < sb_cols { + self.mi_col_starts[i] = start_sb << sb_shift; + i += 1; + start_sb += tile_width_sb; + } + + self.mi_col_starts[i] = self.mi_cols; + self.tile_cols = i as _; + + if self.tile_cols > MAX_TILE_COLS as u32 { + return Err(format!("Invalid tile_cols {}", self.tile_cols)); + } + + /* compute this anyways */ + while i >= 1 { + ti.width_in_sbs_minus_1[i - 1] = + ((self.mi_col_starts[i] - self.mi_col_starts[i - 1] + ((1 << sb_shift) - 1)) + >> sb_shift) + - 1; + i -= 1; + } + + let min_log2_tile_rows = + std::cmp::max(min_log2_tiles.saturating_sub(self.tile_cols_log2), 0); + self.tile_rows_log2 = min_log2_tile_rows; + + while self.tile_rows_log2 < max_log2_tile_rows { + let increment_tile_rows_log_2 = r.0.read_bit()?; + + if increment_tile_rows_log_2 { + self.tile_rows_log2 += 1; + } else { + break; + } + } + + let tile_height_sb = (sb_rows + (1 << self.tile_rows_log2) - 1) >> self.tile_rows_log2; + + let mut i = 0; + let mut start_sb = 0; + + while start_sb < sb_rows { + self.mi_row_starts[i] = start_sb << sb_shift; + i += 1; + start_sb += tile_height_sb; + } + + self.mi_row_starts[i] = self.mi_rows; + self.tile_rows = i as _; + + if self.tile_rows > MAX_TILE_ROWS as u32 { + return Err(format!("Invalid tile_rows {}", self.tile_cols)); + } + + /* compute this anyways */ + while i >= 1 { + ti.height_in_sbs_minus_1[i - 1] = + ((self.mi_row_starts[i] - self.mi_row_starts[i - 1] + ((1 << sb_shift) - 1)) + >> sb_shift) + - 1; + i -= 1; + } + } else { + let mut widest_tile_sb = 0; + let mut start_sb = 0; + let mut i = 0; + + while start_sb < sb_cols { + self.mi_col_starts[i] = start_sb << sb_shift; + + let max_width = std::cmp::min(sb_cols - start_sb, max_tile_width_sb); + ti.width_in_sbs_minus_1[i] = r.read_ns(max_width.try_into().unwrap())?; + + let size_sb = ti.width_in_sbs_minus_1[i] + 1; + widest_tile_sb = std::cmp::max(size_sb, widest_tile_sb); + + start_sb += size_sb; + i += 1; + } + + self.mi_col_starts[i] = self.mi_cols; + self.tile_cols = i as _; + self.tile_cols_log2 = helpers::tile_log2(1, self.tile_cols); + + 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 = std::cmp::max(max_tile_area_sb / widest_tile_sb, 1); + let mut start_sb = 0; + let mut i = 0; + while start_sb < sb_rows { + self.mi_row_starts[i] = start_sb << sb_shift; + let max_height = std::cmp::min(sb_rows - start_sb, max_tile_height_sb); + ti.height_in_sbs_minus_1[i] = r.read_ns(max_height.try_into().unwrap())?; + + let size_sb = ti.height_in_sbs_minus_1[i] + 1; + start_sb += size_sb; + i += 1; + } + + self.mi_row_starts[i] = self.mi_rows; + self.tile_rows = i as _; + self.tile_rows_log2 = helpers::tile_log2(1, self.tile_rows); + } + + if self.tile_cols_log2 > 0 || self.tile_rows_log2 > 0 { + let num_bits: usize = (self.tile_rows_log2 + self.tile_cols_log2).try_into().unwrap(); + ti.context_update_tile_id = r.0.read_bits::(num_bits)?; + + if ti.context_update_tile_id >= self.tile_rows * self.tile_cols { + return Err(format!( + "Invalid context_update_tile_id {}", + ti.context_update_tile_id + )); + } + self.tile_size_bytes = r.0.read_bits::(2)? + 1; + } else { + ti.context_update_tile_id = 0; + } + + ti.mi_col_starts = self.mi_col_starts; + ti.mi_row_starts = self.mi_row_starts; + ti.tile_cols_log2 = self.tile_cols_log2; + ti.tile_cols = self.tile_cols; + ti.tile_rows_log2 = self.tile_rows_log2; + ti.tile_rows = self.tile_rows; + ti.tile_size_bytes = self.tile_size_bytes; + + Ok(()) + } + + fn parse_quantization_params( + r: &mut Reader, + q: &mut QuantizationParams, + num_planes: u32, + separate_uv_delta_q: bool, + ) -> Result<(), String> { + q.base_q_idx = r.0.read_bits::(8)?; + q.delta_q_y_dc = r.read_delta_q()?; + if num_planes > 1 { + if separate_uv_delta_q { + q.diff_uv_delta = r.0.read_bit()?; + } else { + q.diff_uv_delta = false; + } + + q.delta_q_u_dc = r.read_delta_q()?; + q.delta_q_u_ac = r.read_delta_q()?; + if q.diff_uv_delta { + q.delta_q_v_dc = r.read_delta_q()?; + q.delta_q_v_ac = r.read_delta_q()?; + } else { + q.delta_q_v_dc = q.delta_q_u_dc; + q.delta_q_v_ac = q.delta_q_u_ac; + } + } else { + q.delta_q_u_dc = 0; + q.delta_q_u_ac = 0; + q.delta_q_v_dc = 0; + q.delta_q_v_ac = 0; + } + + q.using_qmatrix = r.0.read_bit()?; + if q.using_qmatrix { + q.qm_y = r.0.read_bits::(4)?; + q.qm_u = r.0.read_bits::(4)?; + if !separate_uv_delta_q { + q.qm_v = q.qm_u; + } else { + q.qm_v = r.0.read_bits::(4)?; + } + } + Ok(()) + } + + fn parse_delta_q_params(r: &mut Reader, q: &mut QuantizationParams) -> Result<(), String> { + q.delta_q_res = 0; + q.delta_q_present = false; + if q.base_q_idx > 0 { + q.delta_q_present = r.0.read_bit()?; + } + if q.delta_q_present { + q.delta_q_res = r.0.read_bits::(2)?; + } + + Ok(()) + } + + fn parse_delta_lf_params( + r: &mut Reader, + lf: &mut LoopFilterParams, + delta_q_present: bool, + allow_intrabc: bool, + ) -> Result<(), String> { + lf.delta_lf_present = false; + lf.delta_lf_res = 0; + lf.delta_lf_multi = false; + if delta_q_present { + if !allow_intrabc { + lf.delta_lf_present = r.0.read_bit()?; + } + if lf.delta_lf_present { + lf.delta_lf_res = r.0.read_bits::(2)? as u8; + lf.delta_lf_multi = r.0.read_bit()?; + } + } + Ok(()) + } + + fn parse_segmentation_params( + &self, + r: &mut Reader, + fh: &mut FrameHeaderObu, + ) -> Result<(), String> { + let s = &mut fh.segmentation_params; + s.segmentation_enabled = r.0.read_bit()?; + if s.segmentation_enabled { + if fh.primary_ref_frame == PRIMARY_REF_NONE { + s.segmentation_update_map = true; + s.segmentation_temporal_update = false; + s.segmentation_update_data = true; + } else { + s.segmentation_update_map = r.0.read_bit()?; + if s.segmentation_update_map { + s.segmentation_temporal_update = r.0.read_bit()?; + } + s.segmentation_update_data = r.0.read_bit()?; + } + if s.segmentation_update_data { + for i in 0..MAX_SEGMENTS { + for j in 0..SEG_LVL_MAX { + let feature_enabled = r.0.read_bit()?; + s.feature_enabled[i][j] = feature_enabled; + if feature_enabled { + let bits_to_read = FEATURE_BITS[j]; + let limit = FEATURE_MAX[j]; + let signed = FEATURE_SIGNED[j]; + + if signed { + let feature_value = r.read_su(1 + bits_to_read as usize)?; + let clipped_value = helpers::clip3(-limit, limit, feature_value); + s.feature_data[i][j] = clipped_value as _; + } else { + let feature_value = r.0.read_bits::(bits_to_read as usize)?; + let clipped_value = helpers::clip3( + 0, + limit, + feature_value + .try_into() + .map_err(|_| "Invalid feature_value")?, + ); + s.feature_data[i][j] = clipped_value as _; + } + } + } + } + } else { + /* copy from prev_frame */ + let prev_frame = + &self.ref_info[fh.ref_frame_idx[fh.primary_ref_frame as usize] as usize]; + + if !prev_frame.ref_valid { + return Err("Reference is invalid".into()); + } + + s.feature_enabled = prev_frame.segmentation_params.feature_enabled; + s.feature_data = prev_frame.segmentation_params.feature_data; + } + } else { + for i in 0..MAX_SEGMENTS { + for j in 0..SEG_LVL_MAX { + s.feature_enabled[i][j] = false; + s.feature_data[i][j] = 0; + } + } + } + + 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.feature_enabled[i][j] { + s.last_active_seg_id = i as u8; + if j >= SEG_LVL_REF_FRAME { + s.seg_id_pre_skip = true; + } + } + } + } + + Ok(()) + } + + fn parse_loop_filter_parameters( + r: &mut Reader, + fh: &mut FrameHeaderObu, + num_planes: u32, + ) -> Result<(), String> { + let lf = &mut fh.loop_filter_params; + if fh.coded_lossless || fh.allow_intrabc { + lf.loop_filter_level[0] = 0; + lf.loop_filter_level[1] = 0; + lf.loop_filter_ref_deltas = [1, 0, 0, 0, -1, 0, -1, -1]; + + lf.loop_filter_mode_deltas = Default::default(); + + return Ok(()); + } + + lf.loop_filter_level[0] = r.0.read_bits::(6)? as u8; + lf.loop_filter_level[1] = r.0.read_bits::(6)? as u8; + if num_planes > 1 && (lf.loop_filter_level[0] > 0 || lf.loop_filter_level[1] > 0) { + lf.loop_filter_level[2] = r.0.read_bits::(6)? as u8; + lf.loop_filter_level[3] = r.0.read_bits::(6)? as u8; + } + + lf.loop_filter_sharpness = r.0.read_bits::(3)? as u8; + lf.loop_filter_delta_enabled = r.0.read_bit()?; + if lf.loop_filter_delta_enabled { + lf.loop_filter_delta_update = r.0.read_bit()?; + if lf.loop_filter_delta_update { + for i in 0..TOTAL_REFS_PER_FRAME { + let update_ref_delta = r.0.read_bit()?; + if update_ref_delta { + lf.loop_filter_ref_deltas[i] = r.read_su(7)? as i8; + } + } + + for i in 0..2 { + let update_mode_delta = r.0.read_bit()?; + if update_mode_delta { + lf.loop_filter_mode_deltas[i] = r.read_su(7)? as i8; + } + } + } + } + + Ok(()) + } + + fn parse_cdef_params( + r: &mut Reader, + fh: &mut FrameHeaderObu, + enable_cdef: bool, + num_planes: u32, + ) -> Result<(), String> { + let cdef = &mut fh.cdef_params; + + if fh.coded_lossless || fh.allow_intrabc || !enable_cdef { + cdef.cdef_bits = 0; + cdef.cdef_y_pri_strength[0] = 0; + cdef.cdef_y_sec_strength[0] = 0; + cdef.cdef_uv_pri_strength[0] = 0; + cdef.cdef_uv_sec_strength[0] = 0; + cdef.cdef_damping = 3; + return Ok(()); + } + + cdef.cdef_damping = r.0.read_bits::(2)? + 3; + cdef.cdef_bits = r.0.read_bits::(2)?; + for i in 0..(1 << cdef.cdef_bits) as usize { + cdef.cdef_y_pri_strength[i] = r.0.read_bits::(4)?; + cdef.cdef_y_sec_strength[i] = r.0.read_bits::(2)?; + if cdef.cdef_y_sec_strength[i] == 3 { + cdef.cdef_y_sec_strength[i] += 1; + } + if num_planes > 1 { + cdef.cdef_uv_pri_strength[i] = r.0.read_bits::(4)?; + cdef.cdef_uv_sec_strength[i] = r.0.read_bits::(2)?; + if cdef.cdef_uv_sec_strength[i] == 3 { + cdef.cdef_uv_sec_strength[i] += 1; + } + } + } + + Ok(()) + } + + fn parse_loop_restoration_params( + r: &mut Reader, + fh: &mut FrameHeaderObu, + enable_restoration: bool, + num_planes: u32, + use_128x128_superblock: bool, + subsampling_x: bool, + subsampling_y: bool, + ) -> Result<(), String> { + let lr = &mut fh.loop_restoration_params; + + if fh.all_lossless || fh.allow_intrabc || !enable_restoration { + lr.frame_restoration_type[0] = FrameRestorationType::None; + lr.frame_restoration_type[1] = FrameRestorationType::None; + lr.frame_restoration_type[2] = FrameRestorationType::None; + lr.uses_lr = false; + return Ok(()); + } + + lr.uses_lr = false; + lr.uses_chroma_lr = false; + + const REMAP_LR_TYPE: [FrameRestorationType; 4] = [ + FrameRestorationType::None, + FrameRestorationType::Switchable, + FrameRestorationType::Wiener, + FrameRestorationType::Sgrproj, + ]; + + for i in 0..num_planes as usize { + let lr_type = r.0.read_bits::(2)?; + lr.frame_restoration_type[i] = REMAP_LR_TYPE[lr_type as usize]; + if lr.frame_restoration_type[i] != FrameRestorationType::None { + lr.uses_lr = true; + if i > 0 { + lr.uses_chroma_lr = true; + } + } + } + + if lr.uses_lr { + if use_128x128_superblock { + lr.lr_unit_shift = r.0.read_bits::(1)? as u8 + 1; + } else { + lr.lr_unit_shift = r.0.read_bits::(1)? as u8; + if lr.lr_unit_shift > 0 { + lr.lr_unit_shift += r.0.read_bits::(1)? as u8; + } + } + + lr.loop_restoration_size[0] = RESTORATION_TILESIZE_MAX >> (2 - lr.lr_unit_shift); + if subsampling_x && subsampling_y && lr.uses_chroma_lr { + lr.lr_uv_shift = r.0.read_bits::(1)? as u8; + } else { + lr.lr_uv_shift = 0; + } + + lr.loop_restoration_size[1] = lr.loop_restoration_size[0] >> lr.lr_uv_shift; + lr.loop_restoration_size[2] = lr.loop_restoration_size[0] >> lr.lr_uv_shift; + } + + Ok(()) + } + + fn read_tx_mode(r: &mut Reader, fh: &mut FrameHeaderObu) -> Result<(), String> { + if fh.coded_lossless { + fh.tx_mode = TxMode::Only4x4; + } else { + let tx_mode_select = r.0.read_bit()?; + + if tx_mode_select { + fh.tx_mode = TxMode::Select; + } else { + fh.tx_mode = TxMode::Largest; + } + } + + Ok(()) + } + + fn parse_skip_mode_params( + &self, + r: &mut Reader, + fh: &mut FrameHeaderObu, + enable_order_hint: bool, + order_hint_bits: i32, + ) -> Result<(), String> { + let skip_mode_allowed; + + if fh.frame_is_intra || !fh.reference_select || !enable_order_hint { + skip_mode_allowed = false; + } else { + let mut forward_idx = -1; + let mut backward_idx = -1; + let mut forward_hint = 0; + let mut backward_hint = 0; + for i in 0..REFS_PER_FRAME { + let ref_hint = self.ref_info[fh.ref_frame_idx[i] as usize].ref_order_hint; + if helpers::get_relative_dist( + enable_order_hint, + order_hint_bits, + ref_hint.try_into().unwrap(), + fh.order_hint.try_into().unwrap(), + ) < 0 + && (forward_idx < 0 + || helpers::get_relative_dist( + enable_order_hint, + order_hint_bits, + ref_hint.try_into().unwrap(), + forward_hint, + ) > 0) + { + forward_idx = i32::try_from(i).unwrap(); + forward_hint = ref_hint.try_into().unwrap(); + } else if helpers::get_relative_dist( + enable_order_hint, + order_hint_bits, + ref_hint.try_into().unwrap(), + fh.order_hint.try_into().unwrap(), + ) > 0 + && (backward_idx < 0 || { + helpers::get_relative_dist( + enable_order_hint, + order_hint_bits, + ref_hint.try_into().unwrap(), + backward_hint, + ) < 0 + }) + { + backward_idx = i32::try_from(i).unwrap(); + backward_hint = ref_hint.try_into().unwrap(); + } + } + + if forward_idx < 0 { + skip_mode_allowed = false; + } else if backward_idx >= 0 { + skip_mode_allowed = true; + fh.skip_mode_frame[0] = ReferenceFrameType::Last as u32 + + u32::try_from(std::cmp::min(forward_idx, backward_idx)).unwrap(); + fh.skip_mode_frame[1] = ReferenceFrameType::Last as u32 + + u32::try_from(std::cmp::max(forward_idx, backward_idx)).unwrap(); + } else { + let mut second_forward_idx = -1; + let mut second_forward_hint = 0; + for i in 0..REFS_PER_FRAME { + let ref_hint = self.ref_info[fh.ref_frame_idx[i] as usize].ref_order_hint; + if helpers::get_relative_dist( + enable_order_hint, + order_hint_bits, + ref_hint.try_into().unwrap(), + forward_hint, + ) < 0 + && (second_forward_idx < 0 + || helpers::get_relative_dist( + enable_order_hint, + order_hint_bits, + ref_hint.try_into().unwrap(), + second_forward_hint, + ) > 0) + { + second_forward_idx = i32::try_from(i).unwrap(); + second_forward_hint = ref_hint.try_into().unwrap(); + } + } + + if second_forward_idx < 0 { + skip_mode_allowed = false; + } else { + skip_mode_allowed = true; + fh.skip_mode_frame[0] = ReferenceFrameType::Last as u32 + + u32::try_from(std::cmp::min(forward_idx, second_forward_idx)).unwrap(); + fh.skip_mode_frame[1] = ReferenceFrameType::Last as u32 + + u32::try_from(std::cmp::max(forward_idx, second_forward_idx)).unwrap(); + } + } + } + + if skip_mode_allowed { + fh.skip_mode_present = r.0.read_bit()?; + } else { + fh.skip_mode_present = false; + } + + Ok(()) + } + + fn parse_frame_reference_mode(r: &mut Reader, fh: &mut FrameHeaderObu) -> Result<(), String> { + if fh.frame_is_intra { + fh.reference_select = false; + } else { + fh.reference_select = r.0.read_bit()?; + } + Ok(()) + } + + fn seg_feature_active_idx(seg: &SegmentationParams, idx: u32, feature: u32) -> bool { + seg.segmentation_enabled && seg.feature_enabled[idx as usize][feature as usize] + } + + fn get_qindex(fh: &FrameHeaderObu, ignore_deltaq: bool, segment_id: u32) -> i32 { + let base_q_idx = i32::try_from(fh.quantization_params.base_q_idx).unwrap(); + if Self::seg_feature_active_idx(&fh.segmentation_params, segment_id, SEG_LVL_ALT_Q as u32) { + let data = fh.segmentation_params.feature_data[segment_id as usize][SEG_LVL_ALT_Q]; + let mut qindex = base_q_idx + i32::from(data); + if !ignore_deltaq && fh.quantization_params.delta_q_present { + qindex += i32::try_from(fh.quantization_params.delta_q_res).unwrap(); + } + helpers::clip3(0, 255, qindex) + } else { + base_q_idx + } + } + + fn setup_shear(warp_params: &[i32; 6]) -> Result { + let mut default = true; + for (i, param) in warp_params.iter().enumerate() { + let default_value = if i % 3 == 2 { 1 << WARPEDMODEL_PREC_BITS } else { 0 }; + if *param != default_value { + default = false; + break; + } + } + + /* assume the default params to be valid */ + if default { + return Ok(true); + } + + let alpha0 = helpers::clip3(-32768, 32767, warp_params[2] - (1 << WARPEDMODEL_PREC_BITS)); + let beta0 = helpers::clip3(-32768, 32767, warp_params[3]); + + let (div_shift, div_factor) = helpers::resolve_divisor(warp_params[2])?; + + let v = i64::from(warp_params[4] << WARPEDMODEL_PREC_BITS); + let v = (v * i64::from(div_factor)) as i32; + let gamma0 = helpers::clip3(-32678, 32767, helpers::round2signed(v, div_shift)?); + + let w = warp_params[3] * warp_params[4]; + + let delta0 = helpers::clip3( + -32768, + 32767, + warp_params[5] + - helpers::round2signed(w * div_factor, div_shift)? + - (1 << WARPEDMODEL_PREC_BITS), + ); + + let alpha = + helpers::round2signed(alpha0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS; + let beta = helpers::round2signed(beta0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS; + let gamma = + helpers::round2signed(gamma0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS; + let delta = + helpers::round2signed(delta0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS; + + #[allow(clippy::needless_bool)] + let warp_valid = if 4 * alpha.abs() + 7 * beta.abs() >= (1 << WARPEDMODEL_PREC_BITS) + || 4 * gamma.abs() + 4 * delta.abs() >= (1 << WARPEDMODEL_PREC_BITS) + { + false + } else { + true + }; + + Ok(warp_valid) + } + + fn read_global_param( + reader: &mut Reader, + type_: WarpModelType, + ref_frame: usize, + idx: usize, + allow_high_precision_mv: bool, + prev_gm_params: &[[i32; 6]; NUM_REF_FRAMES], + gm_params: &mut [[i32; 6]; NUM_REF_FRAMES], + ) -> Result<(), String> { + let mut abs_bits = GM_ABS_ALPHA_BITS; + let mut prec_bits = GM_ALPHA_PREC_BITS; + if idx < 2 { + if type_ == WarpModelType::Translation { + abs_bits = GM_ABS_TRANS_ONLY_BITS - !allow_high_precision_mv as u32; + prec_bits = GM_TRANS_ONLY_PREC_BITS - !allow_high_precision_mv as u32; + } else { + abs_bits = GM_ABS_TRANS_BITS; + prec_bits = GM_TRANS_PREC_BITS; + } + } + + let prec_diff = WARPEDMODEL_PREC_BITS - prec_bits; + + let (round, sub) = + if (idx % 3) == 2 { (1 << WARPEDMODEL_PREC_BITS, 1 << prec_bits) } else { (0, 0) }; + + let mx = 1 << abs_bits; + let r = (prev_gm_params[ref_frame][idx] >> prec_diff) - sub; + gm_params[ref_frame][idx] = + (reader.decode_signed_subexp_with_ref(-mx, mx + 1, r)? << prec_diff) + round; + + Ok(()) + } + + fn parse_global_motion_params( + &mut self, + r: &mut Reader, + fh: &mut FrameHeaderObu, + ) -> Result<(), String> { + let gm = &mut fh.global_motion_params; + let mut type_; + let mut prev_gm_params: [[i32; 6]; NUM_REF_FRAMES] = Default::default(); + + for ref_frame in ReferenceFrameType::Last as usize..=ReferenceFrameType::AltRef as usize { + gm.gm_type[ref_frame] = WarpModelType::Identity; + for i in 0..6 { + gm.gm_params[ref_frame][i] = if i % 3 == 2 { 1 << WARPEDMODEL_PREC_BITS } else { 0 } + } + gm.warp_valid[ref_frame] = true; + } + + if fh.frame_is_intra { + return Ok(()); + } + + // Following libgav1: implement part of setup_past_independence() and + // load_previous(), i.e.: the parts that refer to the global motion + // parameters. + if fh.primary_ref_frame == PRIMARY_REF_NONE { + // setup_past_independence() + #[allow(clippy::needless_range_loop)] + for ref_frame in ReferenceFrameType::Last as usize..ReferenceFrameType::AltRef as usize + { + for i in 0..5 { + prev_gm_params[ref_frame][i] = + if i % 3 == 2 { 1 << WARPEDMODEL_PREC_BITS } else { 0 } + } + } + } else { + // load_previous(): + // 1. The variable prevFrame is set equal to ref_frame_idx[ primary_ref_frame ]. + // 2. PrevGmParams is set equal to SavedGmParams[ prevFrame ]. + let prev_frame = fh.ref_frame_idx[fh.primary_ref_frame as usize]; + prev_gm_params = self.ref_info[prev_frame as usize].global_motion_params.gm_params; + } + + for ref_frame in ReferenceFrameType::Last as usize..=ReferenceFrameType::AltRef as usize { + gm.is_global[ref_frame] = r.0.read_bit()?; + if gm.is_global[ref_frame] { + gm.is_rot_zoom[ref_frame] = r.0.read_bit()?; + if gm.is_rot_zoom[ref_frame] { + type_ = WarpModelType::RotZoom; + } else { + gm.is_translation[ref_frame] = r.0.read_bit()?; + if gm.is_translation[ref_frame] { + type_ = WarpModelType::Translation; + } else { + type_ = WarpModelType::Affine; + } + } + } else { + type_ = WarpModelType::Identity; + } + + gm.gm_type[ref_frame] = type_; + if gm.gm_type[ref_frame] as u32 >= WarpModelType::RotZoom as u32 { + Self::read_global_param( + r, + type_, + ref_frame, + 2, + fh.allow_high_precision_mv, + &prev_gm_params, + &mut gm.gm_params, + )?; + + Self::read_global_param( + r, + type_, + ref_frame, + 3, + fh.allow_high_precision_mv, + &prev_gm_params, + &mut gm.gm_params, + )?; + + if type_ == WarpModelType::Affine { + Self::read_global_param( + r, + type_, + ref_frame, + 4, + fh.allow_high_precision_mv, + &prev_gm_params, + &mut gm.gm_params, + )?; + + Self::read_global_param( + r, + type_, + ref_frame, + 5, + fh.allow_high_precision_mv, + &prev_gm_params, + &mut gm.gm_params, + )?; + } else { + gm.gm_params[ref_frame][4] = -gm.gm_params[ref_frame][3]; + gm.gm_params[ref_frame][5] = gm.gm_params[ref_frame][2]; + } + } + + if gm.gm_type[ref_frame] as u32 >= WarpModelType::Translation as u32 { + Self::read_global_param( + r, + type_, + ref_frame, + 0, + fh.allow_high_precision_mv, + &prev_gm_params, + &mut gm.gm_params, + )?; + + Self::read_global_param( + r, + type_, + ref_frame, + 1, + fh.allow_high_precision_mv, + &prev_gm_params, + &mut gm.gm_params, + )?; + } + + gm.warp_valid[ref_frame] = Self::setup_shear(&gm.gm_params[ref_frame])?; + } + + Ok(()) + } + + fn parse_film_grain_parameters( + &self, + r: &mut Reader, + fh: &mut FrameHeaderObu, + film_grain_params_present: bool, + mono_chrome: bool, + subsampling_x: bool, + subsampling_y: bool, + ) -> Result<(), String> { + let fg = &mut fh.film_grain_params; + + if !film_grain_params_present || (!fh.show_frame && !fh.showable_frame) { + *fg = Default::default(); + return Ok(()); + } + + fg.apply_grain = r.0.read_bit()?; + if !fg.apply_grain { + *fg = Default::default(); + return Ok(()); + } + + fg.grain_seed = r.0.read_bits::(16)? as u16; + if fh.frame_type == FrameType::InterFrame { + fg.update_grain = r.0.read_bit()?; + } else { + fg.update_grain = true; + } + + if !fg.update_grain { + fg.film_grain_params_ref_idx = r.0.read_bits::(3)? as u8; + let temp_grain_seed = fg.grain_seed; + + if !fh + .ref_frame_idx + .iter() + .any(|&ref_frame_idx| ref_frame_idx == fg.film_grain_params_ref_idx) + { + return Err("Invalid film_grain_params_ref_idx".into()); + } + + // load_grain_params() + *fg = self.ref_info[fg.film_grain_params_ref_idx as usize].film_grain_params.clone(); + + fg.grain_seed = temp_grain_seed; + + return Ok(()); + } + + fg.num_y_points = r.0.read_bits::(4)? as u8; + fg.point_y_value + .iter_mut() + .zip(fg.point_y_scaling.iter_mut()) + .take(fg.num_y_points as usize) + .try_for_each(|(point_y_value, point_y_scaling)| { + *point_y_value = r.0.read_bits::(8)? as u8; + *point_y_scaling = r.0.read_bits::(8)? as u8; + Ok::<_, String>(()) + })?; + + if mono_chrome { + fg.chroma_scaling_from_luma = false; + } else { + fg.chroma_scaling_from_luma = r.0.read_bit()?; + } + + if mono_chrome + || fg.chroma_scaling_from_luma + || (subsampling_x && subsampling_y && fg.num_y_points == 0) + { + fg.num_cb_points = 0; + fg.num_cr_points = 0; + } else { + fg.num_cb_points = r.0.read_bits::(4)? as u8; + if fg.num_cb_points > 10 { + return Err(format!("Invalid num_cb_points {}", fg.num_cb_points)); + } + + for i in 0..fg.num_cb_points as usize { + fg.point_cb_value[i] = r.0.read_bits::(8)? as u8; + if i > 0 && fg.point_cb_value[i - 1] >= fg.point_cb_value[i] { + return Err(format!("Invalid point_cb_value[{}] {}", i, fg.point_cb_value[i])); + } + fg.point_cb_scaling[i] = r.0.read_bits::(8)? as u8; + } + + fg.num_cr_points = r.0.read_bits::(4)? as u8; + for i in 0..fg.num_cr_points as usize { + fg.point_cr_value[i] = r.0.read_bits::(8)? as u8; + if i > 0 && fg.point_cr_value[i - 1] >= fg.point_cr_value[i] { + return Err(format!("Invalid point_cr_value[{}] {}", i, fg.point_cr_value[i])); + } + fg.point_cr_scaling[i] = r.0.read_bits::(8)? as u8; + } + } + + fg.grain_scaling_minus_8 = r.0.read_bits::(2)? as u8; + fg.ar_coeff_lag = r.0.read_bits::(2)?; + + let num_pos_luma = 2 * fg.ar_coeff_lag * (fg.ar_coeff_lag + 1); + let num_pos_chroma = if fg.num_y_points > 0 { + for i in 0..num_pos_luma as usize { + fg.ar_coeffs_y_plus_128[i] = r.0.read_bits::(8)? as u8; + } + num_pos_luma + 1 + } else { + num_pos_luma + }; + + if fg.chroma_scaling_from_luma || fg.num_cb_points > 0 { + for i in 0..num_pos_chroma as usize { + fg.ar_coeffs_cb_plus_128[i] = r.0.read_bits::(8)? as u8; + } + } + + if fg.chroma_scaling_from_luma || fg.num_cr_points > 0 { + for i in 0..num_pos_chroma as usize { + fg.ar_coeffs_cr_plus_128[i] = r.0.read_bits::(8)? as u8; + } + } + + fg.ar_coeff_shift_minus_6 = r.0.read_bits::(2)? as u8; + fg.grain_scale_shift = r.0.read_bits::(2)? as u8; + + if fg.num_cb_points > 0 { + fg.cb_mult = r.0.read_bits::(8)? as u8; + fg.cb_luma_mult = r.0.read_bits::(8)? as u8; + fg.cb_offset = r.0.read_bits::(9)? as u16; + } + + if fg.num_cr_points > 0 { + fg.cr_mult = r.0.read_bits::(8)? as u8; + fg.cr_luma_mult = r.0.read_bits::(8)? as u8; + fg.cr_offset = r.0.read_bits::(9)? as u16; + } + + fg.overlap_flag = r.0.read_bit()?; + fg.clip_to_restricted_range = r.0.read_bit()?; + + Ok(()) + } + + fn sequence(&self) -> Result<&SequenceHeaderObu, String> { + let Some(seq) = self.sequence_header.as_ref() else { + return Err("No sequence header parsed yet".into()); + }; + + Ok(seq) + } + + fn parse_uncompressed_frame_header(&mut self, obu: &Obu) -> Result { + let mut r = Reader::new(obu.as_ref()); + + let mut fh = FrameHeaderObu { obu_header: obu.header.clone(), ..Default::default() }; + + // Section 6.8.1: It is a requirement of bitstream conformance that a + // sequence header OBU has been received before a frame header OBU. + let &SequenceHeaderObu { + operating_points_cnt_minus_1, + seq_force_integer_mv, + additional_frame_id_length_minus_1, + delta_frame_id_length_minus_2, + decoder_model_info_present_flag, + reduced_still_picture_header, + frame_id_numbers_present_flag, + use_128x128_superblock, + enable_order_hint, + seq_force_screen_content_tools, + order_hint_bits, + enable_cdef, + enable_restoration, + enable_warped_motion, + color_config: + ColorConfig { subsampling_x, subsampling_y, separate_uv_delta_q, mono_chrome, .. }, + timing_info: TimingInfo { equal_picture_interval, .. }, + decoder_model_info: + DecoderModelInfo { + frame_presentation_time_length_minus_1, + buffer_removal_time_length_minus_1, + .. + }, + num_planes, + film_grain_params_present, + .. + } = self.sequence()?; + + let mut id_len = 0; + + if frame_id_numbers_present_flag { + id_len = additional_frame_id_length_minus_1 + delta_frame_id_length_minus_2 + 3; + } + + const ALL_FRAMES: u32 = (1 << NUM_REF_FRAMES) - 1; + + if reduced_still_picture_header { + fh.show_existing_frame = false; + fh.frame_type = FrameType::KeyFrame; + fh.frame_is_intra = true; + fh.show_frame = true; + fh.showable_frame = false; + } else { + fh.show_existing_frame = r.0.read_bit()?; + if matches!(obu.header.obu_type, ObuType::Frame) && fh.show_existing_frame { + return Err("If obu_type is equal to OBU_FRAME, it is a requirement of bitstream conformance that show_existing_frame is equal to 0.".into()); + } + if fh.show_existing_frame { + fh.frame_to_show_map_idx = r.0.read_bits::(3)? as u8; + + if decoder_model_info_present_flag && !equal_picture_interval { + fh.frame_presentation_time = + r.0.read_bits::(frame_presentation_time_length_minus_1 as usize + 1)?; + } + + let ref_frame = &self.ref_info[fh.frame_to_show_map_idx as usize]; + + fh.refresh_frame_flags = 0; + if frame_id_numbers_present_flag { + if id_len == 0 { + return Err(format!("Invalid id_len {}", id_len)); + } + fh.display_frame_id = r.0.read_bits::(id_len.try_into().unwrap())?; + if ref_frame.display_frame_id != fh.display_frame_id || !ref_frame.ref_valid { + return Err("Invalid display_frame_id".into()); + } + } + + if !ref_frame.showable_frame { + return Err("Invalid bitstream: can't show this past frame".into()); + } + + // In decode_frame_wrapup(): + // + // Otherwise (show_existing_frame is equal to 1), if frame_type + // is equal to KEY_FRAME, the reference frame loading process as + // specified in section 7.21 is invoked (this process loads + // frame state from the reference frames into the current frame + // state variables) + // + // The following ordered steps now apply: + // + // 1. The reference frame update process as specified in section + // 7.20 is invoked (this process saves the current frame state + // into the reference frames). + // + // 2. If show_frame is equal to 1 or show_existing_frame is + // equal to 1, the output process as specified in section 7.18 + // is invoked (this will output the current frame or a saved + // frame). + // + // We implement 1. here while 2. is left to the actual decoder + self.load_reference_frame(&mut fh)?; + if fh.frame_type == FrameType::KeyFrame { + fh.refresh_frame_flags = ALL_FRAMES; + } + + if film_grain_params_present { + // load_grain_params() + fh.film_grain_params = + self.ref_info[fh.frame_to_show_map_idx as usize].film_grain_params.clone(); + } + + // See 5.10. + if matches!(obu.header.obu_type, ObuType::Frame) { + r.byte_alignment()?; + } + + fh.header_bytes = usize::try_from(r.0.position() / 8).unwrap(); + return Ok(fh); + } + + fh.frame_type = FrameType::try_from(r.0.read_bits::(2)?)?; + fh.frame_is_intra = + matches!(fh.frame_type, FrameType::IntraOnlyFrame | FrameType::KeyFrame); + + fh.show_frame = r.0.read_bit()?; + + if fh.show_frame && decoder_model_info_present_flag && equal_picture_interval { + fh.frame_presentation_time = + r.0.read_bits::(frame_presentation_time_length_minus_1 as usize + 1)?; + } + + if fh.show_frame { + fh.showable_frame = !matches!(fh.frame_type, FrameType::KeyFrame); + } else { + fh.showable_frame = r.0.read_bit()?; + } + + if fh.frame_type == FrameType::SwitchFrame + || (fh.frame_type == FrameType::KeyFrame && fh.show_frame) + { + fh.error_resilient_mode = true; + } else { + fh.error_resilient_mode = r.0.read_bit()?; + } + } + + if fh.frame_type == FrameType::KeyFrame && fh.show_frame { + for i in 0..NUM_REF_FRAMES { + self.ref_info[i].ref_valid = false; + self.ref_info[i].ref_order_hint = 0; + } + for i in 0..REFS_PER_FRAME { + fh.order_hints[ReferenceFrameType::Last as usize + i] = 0; + } + } + + fh.disable_cdf_update = r.0.read_bit()?; + if seq_force_screen_content_tools == SELECT_SCREEN_CONTENT_TOOLS as u32 { + fh.allow_screen_content_tools = r.0.read_bit()? as u32; + } else { + fh.allow_screen_content_tools = seq_force_screen_content_tools; + } + + if fh.allow_screen_content_tools > 0 { + if seq_force_integer_mv == SELECT_INTEGER_MV as u32 { + fh.force_integer_mv = r.0.read_bit()? as u32; + } else { + fh.force_integer_mv = seq_force_integer_mv; + } + } else { + fh.force_integer_mv = 0; + } + + if fh.frame_is_intra { + fh.force_integer_mv = 1; + } + + if frame_id_numbers_present_flag { + self.prev_frame_id = self.current_frame_id; + self.current_frame_id = r.0.read_bits::(id_len.try_into().unwrap())?; + fh.current_frame_id = self.current_frame_id; + + /* conformance checking, as per aom */ + let have_prev_frame_id = + !(self.is_first_frame || fh.frame_type == FrameType::KeyFrame && fh.show_frame); + + if have_prev_frame_id { + let frame_id_length = + additional_frame_id_length_minus_1 + delta_frame_id_length_minus_2 + 3; + + let diff_frame_id = if self.current_frame_id > self.prev_frame_id { + self.current_frame_id - self.prev_frame_id + } else { + if frame_id_length > 16 { + return Err(format!("Invalid frame_id_length {}", frame_id_length)); + } + (1 << frame_id_length) + self.current_frame_id - self.prev_frame_id + }; + + if self.prev_frame_id == self.current_frame_id + || diff_frame_id >= (1 << (frame_id_length - 1)) + { + return Err(format!( + "Invalid frame_id: prev_frame_id = {}, current_frame_id = {}", + self.prev_frame_id, self.current_frame_id + )); + } + } + + /* mark_ref_frames (idLen) */ + let diff_len = delta_frame_id_length_minus_2 + 2; + let shifted_diff_len = 1 << diff_len; + let shifted_id_len = 1 << id_len; + + for i in 0..NUM_REF_FRAMES { + if self.current_frame_id > shifted_diff_len { + if self.ref_info[i].ref_frame_id > self.current_frame_id + || self.ref_info[i].ref_frame_id + < (self.current_frame_id - shifted_diff_len) + { + self.ref_info[i].ref_valid = false; + } + } else if self.ref_info[i].ref_frame_id > self.current_frame_id + && self.ref_info[i].ref_frame_id + < shifted_id_len + self.current_frame_id - shifted_diff_len + { + self.ref_info[i].ref_valid = false; + } + } + } else { + self.current_frame_id = 0; + self.prev_frame_id = self.current_frame_id; + fh.current_frame_id = self.current_frame_id; + } + + if fh.frame_type == FrameType::SwitchFrame { + fh.frame_size_override_flag = true; + } else if reduced_still_picture_header { + fh.frame_size_override_flag = false; + } else { + fh.frame_size_override_flag = r.0.read_bit()?; + } + + fh.order_hint = r.0.read_bits::(order_hint_bits.try_into().unwrap())?; + + if fh.frame_is_intra || fh.error_resilient_mode { + fh.primary_ref_frame = PRIMARY_REF_NONE; + } else { + fh.primary_ref_frame = r.0.read_bits::(3)?; + } + + let operating_points = &self.sequence()?.operating_points; + if decoder_model_info_present_flag { + fh.buffer_removal_time_present_flag = r.0.read_bit()?; + if fh.buffer_removal_time_present_flag { + #[allow(clippy::needless_range_loop)] + for op_num in 0..=operating_points_cnt_minus_1 as usize { + if operating_points[op_num].decoder_model_present_for_this_op { + let op_pt_idc = operating_points[op_num].idc; + let in_temporal_layer = (op_pt_idc >> fh.obu_header.temporal_id) & 1 != 0; + let in_spatial_layer = + (op_pt_idc >> (fh.obu_header.spatial_id + 8)) & 1 != 0; + + if op_pt_idc == 0 || (in_temporal_layer && in_spatial_layer) { + let n = buffer_removal_time_length_minus_1 + 1; + fh.buffer_removal_time[op_num] = r.0.read_bits::(n as usize)?; + } + } + } + } + } + + fh.allow_high_precision_mv = false; + fh.use_ref_frame_mvs = false; + fh.allow_intrabc = false; + if fh.frame_type == FrameType::SwitchFrame + || (fh.frame_type == FrameType::KeyFrame && fh.show_frame) + { + fh.refresh_frame_flags = ALL_FRAMES; + } else { + fh.refresh_frame_flags = r.0.read_bits::(8)?; + } + + /* equivalent boolean expression */ + if (!fh.frame_is_intra || fh.refresh_frame_flags != ALL_FRAMES) + && fh.error_resilient_mode + && enable_order_hint + { + for i in 0..NUM_REF_FRAMES { + fh.ref_order_hint[i] = r.0.read_bits::(order_hint_bits.try_into().unwrap())?; + if fh.ref_order_hint[i] != self.ref_info[i].ref_order_hint { + self.ref_info[i].ref_valid = false; + } + } + } + + if fh.frame_is_intra { + self.parse_frame_size(&mut fh, &mut r)?; + Self::parse_render_size(&mut fh, &mut r)?; + if fh.allow_screen_content_tools > 0 && fh.upscaled_width == fh.frame_width { + fh.allow_intrabc = r.0.read_bit()?; + } + } else { + if !enable_order_hint { + fh.frame_refs_short_signaling = false; + } else { + fh.frame_refs_short_signaling = r.0.read_bit()?; + if fh.frame_refs_short_signaling { + fh.last_frame_idx = r.0.read_bits::(3)? as u8; + fh.gold_frame_idx = r.0.read_bits::(3)? as u8; + let ref_order_hints = self + .ref_info + .iter() + .map(|i| i.ref_order_hint) + .collect::>() + .try_into() + .unwrap(); + self.set_frame_refs(&mut fh, &ref_order_hints)?; + } + } + + let mut expected_frame_id = [0; REFS_PER_FRAME]; + #[allow(clippy::needless_range_loop)] + for i in 0..REFS_PER_FRAME { + if !fh.frame_refs_short_signaling { + fh.ref_frame_idx[i] = r.0.read_bits::(3)?.try_into().unwrap(); + } + + if frame_id_numbers_present_flag { + /* DeltaFrameId */ + let delta_frame_id = + r.0.read_bits::(delta_frame_id_length_minus_2 as usize + 2)? + 1; + + if id_len == 0 { + return Err(format!("Invalid id_len {}", id_len)); + } + + let shifted_id_len = 1 << id_len; + + expected_frame_id[i] = + (self.current_frame_id + shifted_id_len - delta_frame_id) % shifted_id_len; + + let actual_frame_id = self.ref_info[fh.ref_frame_idx[i] as usize].ref_frame_id; + + if expected_frame_id[i] != actual_frame_id { + return Err(format!( + "Invalid frame id, expected {} got {}", + expected_frame_id[i], actual_frame_id + )); + } + } + } + + if fh.frame_size_override_flag && !fh.error_resilient_mode { + self.frame_size_with_refs(&mut fh, &mut r)?; + } else { + self.parse_frame_size(&mut fh, &mut r)?; + Self::parse_render_size(&mut fh, &mut r)?; + } + + if fh.force_integer_mv > 0 { + fh.allow_high_precision_mv = false; + } else { + fh.allow_high_precision_mv = r.0.read_bit()?; + } + + /* read_interpolation_filter */ + fh.is_filter_switchable = r.0.read_bit()?; + if fh.is_filter_switchable { + fh.interpolation_filter = InterpolationFilter::Switchable; + } else { + fh.interpolation_filter = InterpolationFilter::try_from(r.0.read_bits::(2)?)?; + } + + fh.is_motion_mode_switchable = r.0.read_bit()?; + + if fh.error_resilient_mode || !self.sequence()?.enable_ref_frame_mvs { + fh.use_ref_frame_mvs = false; + } else { + fh.use_ref_frame_mvs = r.0.read_bit()?; + } + + for i in 0..REFS_PER_FRAME { + let ref_frame = ReferenceFrameType::Last as usize + i; + let hint = self.ref_info[fh.ref_frame_idx[i] as usize].ref_order_hint; + fh.order_hints[ref_frame] = hint; + + if !enable_order_hint { + fh.ref_frame_sign_bias[i] = false; + } else { + fh.ref_frame_sign_bias[i] = helpers::get_relative_dist( + enable_order_hint, + order_hint_bits, + hint.try_into().unwrap(), + fh.order_hint.try_into().unwrap(), + ) > 0; + } + } + } + + if reduced_still_picture_header || fh.disable_cdf_update { + fh.disable_frame_end_update_cdf = true; + } else { + fh.disable_frame_end_update_cdf = r.0.read_bit()?; + } + + if fh.primary_ref_frame == PRIMARY_REF_NONE { + Self::setup_past_independence(&mut fh); + } else { + /* load from the past reference */ + let prev_frame = + &self.ref_info[fh.ref_frame_idx[fh.primary_ref_frame as usize] as usize]; + + if !prev_frame.ref_valid { + return Err("Reference is invalid".into()); + } + + /* load_loop_filter_params: load ref_deltas and mode_deltas */ + fh.loop_filter_params.loop_filter_ref_deltas = + prev_frame.loop_filter_params.loop_filter_ref_deltas; + fh.loop_filter_params.loop_filter_mode_deltas = + prev_frame.loop_filter_params.loop_filter_mode_deltas; + + /* load_segmentation_params: load feature_enabled and feature_data */ + fh.segmentation_params.feature_enabled = prev_frame.segmentation_params.feature_enabled; + fh.segmentation_params.feature_data = prev_frame.segmentation_params.feature_data; + } + + // TODO: we can live without this for now. + // if fh.use_ref_frame_mvs { + // // motion_field_estimators() + // } + + self.parse_tile_info(&mut r, &mut fh.tile_info)?; + Self::parse_quantization_params( + &mut r, + &mut fh.quantization_params, + num_planes, + separate_uv_delta_q, + )?; + self.parse_segmentation_params(&mut r, &mut fh)?; + Self::parse_delta_q_params(&mut r, &mut fh.quantization_params)?; + Self::parse_delta_lf_params( + &mut r, + &mut fh.loop_filter_params, + fh.quantization_params.delta_q_present, + fh.allow_intrabc, + )?; + + fh.coded_lossless = true; + for segment_id in 0..MAX_SEGMENTS { + let q_index = Self::get_qindex(&fh, true, segment_id as _); + let q = &fh.quantization_params; + fh.lossless_array[segment_id] = q_index == 0 + && q.delta_q_y_dc == 0 + && q.delta_q_u_ac == 0 + && q.delta_q_u_dc == 0 + && q.delta_q_v_ac == 0 + && q.delta_q_v_dc == 0; + if !fh.lossless_array[segment_id] { + fh.coded_lossless = false; + } + if q.using_qmatrix { + if fh.lossless_array[segment_id] { + fh.seg_qm_level[0][segment_id] = 15; + fh.seg_qm_level[1][segment_id] = 15; + fh.seg_qm_level[2][segment_id] = 15; + } else { + fh.seg_qm_level[0][segment_id] = q.qm_y; + fh.seg_qm_level[1][segment_id] = q.qm_u; + fh.seg_qm_level[2][segment_id] = q.qm_v; + } + } + } + + fh.all_lossless = fh.coded_lossless && fh.frame_width == fh.upscaled_width; + Self::parse_loop_filter_parameters(&mut r, &mut fh, num_planes)?; + Self::parse_cdef_params(&mut r, &mut fh, enable_cdef, num_planes)?; + Self::parse_loop_restoration_params( + &mut r, + &mut fh, + enable_restoration, + num_planes, + use_128x128_superblock, + subsampling_x, + subsampling_y, + )?; + Self::read_tx_mode(&mut r, &mut fh)?; + Self::parse_frame_reference_mode(&mut r, &mut fh)?; + self.parse_skip_mode_params(&mut r, &mut fh, enable_order_hint, order_hint_bits)?; + + if fh.frame_is_intra || fh.error_resilient_mode || !enable_warped_motion { + fh.allow_warped_motion = false; + } else { + fh.allow_warped_motion = r.0.read_bit()?; + } + + fh.reduced_tx_set = r.0.read_bit()?; + self.parse_global_motion_params(&mut r, &mut fh)?; + self.parse_film_grain_parameters( + &mut r, + &mut fh, + film_grain_params_present, + mono_chrome, + subsampling_x, + subsampling_y, + )?; + + Self::skip_and_check_trailing_bits(&mut r, obu)?; + + // See 5.10 + if matches!(obu.header.obu_type, ObuType::Frame) { + r.byte_alignment()?; + } + + fh.header_bytes = usize::try_from(r.0.position() / 8).unwrap(); + Ok(fh) + } + + fn parse_tile_group_obu<'a>(&mut self, obu: Obu<'a>) -> Result, String> { + let mut tg = TileGroupObu { obu, ..Default::default() }; + + let mut r = Reader::new(tg.obu.as_ref()); + + if r.0.num_bits_left() % 8 != 0 { + return Err("Bitstream is not byte aligned".into()); + } + + let mut sz: u64 = r.0.num_bits_left() as u64 / 8; + + let num_tiles = self.tile_rows * self.tile_cols; + let start_bit_pos = r.0.position(); + + if num_tiles > 1 { + tg.tile_start_and_end_present_flag = r.0.read_bit()?; + } + + if num_tiles == 1 || !tg.tile_start_and_end_present_flag { + tg.tg_start = 0; + tg.tg_end = num_tiles - 1; + } else { + let tile_bits = (self.tile_cols_log2 + self.tile_rows_log2) as usize; + tg.tg_start = r.0.read_bits::(tile_bits)?; + tg.tg_end = r.0.read_bits::(tile_bits)?; + } + + r.byte_alignment()?; + + let end_bit_pos = r.0.position(); + let header_bytes = (end_bit_pos - start_bit_pos) / 8; + sz -= header_bytes; + + let mut tile_num = tg.tg_start; + while tile_num <= tg.tg_end { + let tile_row = tile_num / self.tile_cols; + let tile_col = tile_num % self.tile_cols; + let last_tile = tile_num == tg.tg_end; + let tile_size; + + if last_tile { + tile_size = u32::try_from(sz).unwrap(); + } else { + tile_size = r.0.read_le::(self.tile_size_bytes.try_into().unwrap())? + 1; + sz -= u64::from(tile_size + self.tile_size_bytes); + } + + let tile = Tile { + tile_offset: u32::try_from(r.0.position()).unwrap() / 8, + tile_size, + tile_row, + tile_col, + mi_row_start: self.mi_row_starts[tile_row as usize], + mi_row_end: self.mi_row_starts[tile_row as usize + 1], + mi_col_start: self.mi_row_starts[tile_col as usize], + mi_col_end: self.mi_row_starts[tile_col as usize + 1], + }; + + tg.tiles.push(tile); + + // init_symbol, decode_tile() and exit_symbol() left to the accelerator. + + // Skip the actual tile data + if tile_num < tg.tg_end { + r.0.skip_bits(tile_size as usize * 8)?; + } + + tile_num += 1; + } + + if tg.tg_end == num_tiles - 1 { + // left to the accelerator: + // if ( !disable_frame_end_update_cdf ) { + // frame_end_update_cdf( ) + // } + // decode_frame_wrapup( ) + self.seen_frame_header = false; + } + + Ok(tg) + } + + fn parse_frame_obu<'a>(&mut self, obu: Obu<'a>) -> Result, String> { + if !matches!(obu.header.obu_type, ObuType::Frame) { + return Err(format!("Expected a FrameOBU, got {:?}", obu.header.obu_type)); + } + + let frame_header_obu = self.parse_frame_header_obu(&obu)?; + let obu = Obu { + header: obu.header, + data: match obu.data { + Cow::Borrowed(d) => Cow::Borrowed(&d[frame_header_obu.header_bytes..]), + Cow::Owned(d) => Cow::Owned(d[frame_header_obu.header_bytes..].to_owned()), + }, + bytes_used: obu.bytes_used, + }; + let tile_group_obu = self.parse_tile_group_obu(obu)?; + + Ok(FrameObu { header: frame_header_obu, tile_group: tile_group_obu }) + } + + pub fn parse_frame_header_obu(&mut self, obu: &Obu) -> Result { + if !matches!(obu.header.obu_type, ObuType::FrameHeader | ObuType::Frame) { + return Err(format!("Expected a FrameHeaderOBU, got {:?}", obu.header.obu_type)); + } + + if self.seen_frame_header { + Ok(self + .last_frame_header + .clone() + .take() + .ok_or::("Broken stream: no previous frame header to copy".into())?) + } else { + self.seen_frame_header = true; + let header = self.parse_uncompressed_frame_header(obu)?; + if header.show_existing_frame { + self.last_frame_header = None; + self.seen_frame_header = false; + } else { + /* TileNum = 0 */ + self.seen_frame_header = true; + self.last_frame_header = Some(header.clone()); + } + + Ok(header) + } + } + + /// Implements 7.20. This function should be called right after decoding a + /// frame. + pub fn ref_frame_update(&mut self, fh: &FrameHeaderObu) -> Result<(), String> { + // This was found as a bug otherwise by Nicolas Dufresne in GStreamer's + // av1parse. + if fh.show_existing_frame && !matches!(fh.frame_type, FrameType::KeyFrame) { + return Ok(()); + } + + if matches!(fh.frame_type, FrameType::IntraOnlyFrame) && fh.refresh_frame_flags == 0xff { + return Err("Intra-only frames cannot refresh all of the DPB as per the spec.".into()); + } + + let &SequenceHeaderObu { + color_config: ColorConfig { subsampling_x, subsampling_y, .. }, + film_grain_params_present, + bit_depth, + .. + } = self.sequence()?; + + for (i, ref_info) in self.ref_info.iter_mut().enumerate() { + if ((fh.refresh_frame_flags >> i) & 1) != 0 { + ref_info.ref_valid = true; + + ref_info.ref_frame_id = fh.current_frame_id; + ref_info.ref_frame_type = fh.frame_type; + ref_info.ref_upscaled_width = fh.upscaled_width; + ref_info.ref_frame_width = fh.frame_width; + ref_info.ref_frame_height = fh.frame_height; + ref_info.ref_render_width = fh.render_width; + ref_info.ref_render_height = fh.render_height; + ref_info.ref_order_hint = fh.order_hint; + ref_info.ref_mi_cols = self.mi_cols; + ref_info.ref_mi_rows = self.mi_rows; + ref_info.ref_subsampling_x = subsampling_x; + ref_info.ref_subsampling_y = subsampling_y; + ref_info.ref_bit_depth = bit_depth; + ref_info.segmentation_params = fh.segmentation_params.clone(); + ref_info.global_motion_params = fh.global_motion_params.clone(); + ref_info.loop_filter_params = fh.loop_filter_params.clone(); + ref_info.tile_info = fh.tile_info.clone(); + ref_info.display_frame_id = fh.display_frame_id; + ref_info.showable_frame = fh.showable_frame; + + if film_grain_params_present { + ref_info.film_grain_params = fh.film_grain_params.clone(); + } + } + } + + Ok(()) + } + + pub fn highest_operating_point(&self) -> Option { + if self.operating_point_idc == 0 { + /* No scalability information, all OBUs must be decoded */ + None + } else { + Some(helpers::floor_log2((self.operating_point_idc >> 8) as u32)) + } + } + + /// Fully parse an OBU. + pub fn parse_obu<'a>(&mut self, obu: Obu<'a>) -> Result, String> { + match obu.header.obu_type { + ObuType::Reserved => Ok(ParsedObu::Reserved), + ObuType::SequenceHeader => { + self.parse_sequence_header_obu(&obu).map(ParsedObu::SequenceHeader) + } + ObuType::TemporalDelimiter => { + self.parse_temporal_delimiter_obu().map(|_| ParsedObu::TemporalDelimiter) + } + ObuType::FrameHeader => self.parse_frame_header_obu(&obu).map(ParsedObu::FrameHeader), + ObuType::TileGroup => self.parse_tile_group_obu(obu).map(ParsedObu::TileGroup), + ObuType::Metadata => Ok(ParsedObu::Metadata), + ObuType::Frame => self.parse_frame_obu(obu).map(ParsedObu::Frame), + ObuType::RedundantFrameHeader => Ok(ParsedObu::RedundantFrameHeader), + ObuType::TileList => Ok(ParsedObu::TileList), + ObuType::Reserved2 => Ok(ParsedObu::Reserved2), + ObuType::Reserved3 => Ok(ParsedObu::Reserved3), + ObuType::Reserved4 => Ok(ParsedObu::Reserved4), + ObuType::Reserved5 => Ok(ParsedObu::Reserved5), + ObuType::Reserved6 => Ok(ParsedObu::Reserved6), + ObuType::Reserved7 => Ok(ParsedObu::Reserved7), + ObuType::Padding => Ok(ParsedObu::Padding), + } + } +} + +impl Default for Parser { + fn default() -> Self { + Self { + stream_format: StreamFormat::LowOverhead, + operating_point: Default::default(), + seen_frame_header: Default::default(), + last_frame_header: Default::default(), + operating_point_idc: Default::default(), + should_probe_for_annexb: true, + is_first_frame: Default::default(), + mi_cols: Default::default(), + mi_rows: Default::default(), + prev_frame_id: Default::default(), + current_frame_id: Default::default(), + ref_info: Default::default(), + mi_col_starts: [0; MAX_TILE_COLS + 1], + mi_row_starts: [0; MAX_TILE_ROWS + 1], + tile_cols_log2: Default::default(), + tile_cols: Default::default(), + tile_rows_log2: Default::default(), + tile_rows: Default::default(), + tile_size_bytes: Default::default(), + sequence_header: Default::default(), + } + } +} + +impl Clone for Parser { + fn clone(&self) -> Self { + let sequence_header = self.sequence_header.as_ref().map(|s| Rc::new((**s).clone())); + + Self { + stream_format: self.stream_format.clone(), + operating_point: self.operating_point, + seen_frame_header: self.seen_frame_header, + last_frame_header: self.last_frame_header.clone(), + operating_point_idc: self.operating_point_idc, + should_probe_for_annexb: self.should_probe_for_annexb, + is_first_frame: self.is_first_frame, + ref_info: self.ref_info.clone(), + mi_cols: self.mi_cols, + mi_rows: self.mi_rows, + prev_frame_id: self.prev_frame_id, + current_frame_id: self.current_frame_id, + mi_col_starts: self.mi_col_starts, + mi_row_starts: self.mi_row_starts, + tile_cols_log2: self.tile_cols_log2, + tile_cols: self.tile_cols, + tile_rows_log2: self.tile_rows_log2, + tile_rows: self.tile_rows, + tile_size_bytes: self.tile_size_bytes, + sequence_header, + } + } +} + +#[cfg(test)] +mod tests { + use crate::bitstream_utils::IvfIterator; + use crate::codec::av1::parser::{ObuAction, Parser, StreamFormat}; + + use super::ObuType; + + /// Same as test-25fps.av1.ivf from Chromium + const STREAM_TEST_25_FPS: &[u8] = include_bytes!("test_data/test-25fps.ivf.av1"); + + /// Encoded with + /// + /// gst-launch-1.0 videotestsrc num-buffers=1 ! + /// video/x-raw,format=I420,width=64,height=64 ! filesink + /// location=aom_input.yuv + /// + /// And: + /// + /// aomenc -p 1 --ivf -w 64 -h 64 -o av1-annexb.ivf.av1 aom_input.yuv --annexb=1 + const STREAM_ANNEXB: &[u8] = include_bytes!("test_data/av1-annexb.ivf.av1"); + + #[test] + fn parse_test25fps() { + let mut parser = Parser::default(); + let ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS); + let mut num_obus = 0; + + for packet in ivf_iter { + let mut consumed = 0; + + while let Ok(obu) = parser.read_obu(&packet[consumed..]) { + let obu = match obu { + ObuAction::Process(obu) => obu, + // This OBU should be dropped. + ObuAction::Drop(length) => { + consumed += usize::try_from(length).unwrap(); + continue; + } + }; + consumed += obu.bytes_used; + num_obus += 1; + } + } + + // Manually checked with GStreamer under GDB by using a hitcount on + // "gst_av1_parse_identify_one_obu" *after* the stream format has been + // detected. + assert_eq!(num_obus, 525); + } + + #[test] + /// Test that we can correctly identify streams in both "low-overhead" and + /// Annex B formats. + fn parse_annexb() { + let mut parser = Parser::default(); + let mut ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS); + let packet = ivf_iter.next().unwrap(); + + parser.read_obu(packet).unwrap(); + assert!(matches!(parser.stream_format, StreamFormat::LowOverhead)); + + let mut parser = Parser::default(); + let mut ivf_iter = IvfIterator::new(STREAM_ANNEXB); + let packet = ivf_iter.next().unwrap(); + + parser.read_obu(packet).unwrap(); + assert!(matches!(parser.stream_format, StreamFormat::AnnexB { .. })); + } + + #[test] + /// Test that we can correctly identify streams in both "low-overhead" and + /// Annex B formats and identify all the OBUs in the stream until the end. + fn parse_annexb_full() { + let mut parser = Parser::default(); + let ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS); + + for packet in ivf_iter { + let mut consumed = 0; + + while let Ok(obu) = parser.read_obu(&packet[consumed..]) { + let obu = match obu { + ObuAction::Process(obu) => obu, + // This OBU should be dropped. + ObuAction::Drop(length) => { + consumed += usize::try_from(length).unwrap(); + continue; + } + }; + assert!(matches!(parser.stream_format, StreamFormat::LowOverhead)); + consumed += obu.bytes_used; + } + } + + let mut parser = Parser::default(); + let ivf_iter = IvfIterator::new(STREAM_ANNEXB); + let mut num_obus = 0; + + for packet in ivf_iter { + let mut consumed = 0; + + while let Ok(obu) = parser.read_obu(&packet[consumed..]) { + let obu = match obu { + ObuAction::Process(obu) => obu, + // This OBU should be dropped. + ObuAction::Drop(length) => { + consumed += usize::try_from(length).unwrap(); + continue; + } + }; + assert!(matches!(parser.stream_format, StreamFormat::AnnexB { .. })); + consumed += obu.bytes_used; + num_obus += 1; + } + } + + assert_eq!(num_obus, 3); + let annexb_state = match parser.stream_format { + StreamFormat::AnnexB(annexb_state) => annexb_state, + _ => panic!("Wrong StreamFormat, expected AnnexB"), + }; + assert_eq!(annexb_state.temporal_unit_consumed, annexb_state.temporal_unit_size); + assert_eq!(annexb_state.frame_unit_consumed, annexb_state.frame_unit_size); + } + + #[test] + fn parse_test25fps_obus() { + let mut parser = Parser::default(); + let ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS); + + for packet in ivf_iter { + let mut consumed = 0; + + while let Ok(obu) = parser.read_obu(&packet[consumed..]) { + let obu = match obu { + ObuAction::Process(obu) => obu, + // This OBU should be dropped. + ObuAction::Drop(length) => { + consumed += usize::try_from(length).unwrap(); + continue; + } + }; + + let data_len = obu.bytes_used; + + match obu.header.obu_type { + ObuType::SequenceHeader => { + parser.parse_sequence_header_obu(&obu).unwrap(); + } + ObuType::FrameHeader | ObuType::RedundantFrameHeader => { + let fh = parser.parse_frame_header_obu(&obu).unwrap(); + parser.ref_frame_update(&fh).unwrap(); + } + ObuType::TileGroup => { + parser.parse_tile_group_obu(obu).unwrap(); + } + ObuType::Frame => { + let frame = parser.parse_frame_obu(obu).unwrap(); + parser.ref_frame_update(&frame.header).unwrap(); + } + _ => {} + }; + + consumed += data_len; + } + } + } +} diff --git a/vendor/cros-codecs/src/codec/av1/reader.rs b/vendor/cros-codecs/src/codec/av1/reader.rs new file mode 100644 index 00000000..38a96265 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/reader.rs @@ -0,0 +1,249 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::bitstream_utils::BitReader; + +use crate::codec::av1::helpers; + +use super::parser::AnnexBState; + +pub(crate) struct Reader<'a>(pub BitReader<'a>); + +impl<'a> Reader<'a> { + pub fn new(data: &'a [u8]) -> Self { + Self(BitReader::new(data, false)) + } + + /// Implements uvlc(): Variable length unsigned n-bit number appearing + /// directly in the bitstream. See 4.10.3 + pub fn read_uvlc(&mut self) -> Result { + let mut leading_zeroes = 0; + loop { + let done = self.0.read_bit()?; + + if done { + break; + } + + leading_zeroes += 1; + } + + if leading_zeroes >= 32 { + return Ok(u32::MAX); + } + + let value = self.0.read_bits::(leading_zeroes)?; + Ok(value + (1 << leading_zeroes) - 1) + } + + /// Implements leb128(): Unsigned integer represented by a variable number + /// of little-endian bytes. See 4.10.5 + pub fn read_leb128(&mut self) -> Result { + let mut value = 0u64; + + for i in 0..8 { + let byte = u64::from(self.0.read_bits_aligned::(8)?); + value |= (byte & 0x7f) << (i * 7); + + if byte & 0x80 == 0 { + break; + } + } + + Ok(value as u32) + } + + /// Implements su(n): Signed integer converted from an n bits unsigned + /// integer in the bitstream. (The unsigned integer corresponds to the + /// bottom n bits of the signed integer.). See 4.10.6 + pub fn read_su(&mut self, num_bits: usize) -> Result { + let mut value: i32 = self + .0 + .read_bits::(num_bits)? + .try_into() + .map_err(|_| String::from("Read more than 31 signed bits!"))?; + let sign_mask = 1 << (num_bits - 1); + + if (value & sign_mask) != 0 { + value -= 2 * sign_mask; + } + + Ok(value) + } + + /// Implements ns(n): Unsigned encoded integer with maximum number of values + /// n (i.e. output in range 0..n-1). See 4.10.7 + pub fn read_ns(&mut self, num_bits: usize) -> Result { + let w = helpers::floor_log2(num_bits as u32) + 1; + let m = (1 << w) - num_bits as u32; + let v = self.0.read_bits::( + usize::try_from(w).map_err(|_| String::from("Invalid num_bits"))? - 1, + )?; + + if v < m.into() { + return Ok(v); + } + + let extra_bit = self.0.read_bit()?; + Ok((v << 1) - u32::from(m) + u32::from(extra_bit)) + } + + /// Implements 5.9.13: Delta quantizer syntax. + pub fn read_delta_q(&mut self) -> Result { + let delta_coded = self.0.read_bit()?; + + if delta_coded { + self.read_su(7) + } else { + Ok(0) + } + } + + pub fn more_data_in_bitstream(&mut self) -> bool { + self.0.num_bits_left() > 0 + } + + pub(crate) fn consumed(&self, start_pos: u32) -> u32 { + (self.0.position() / 8) as u32 - start_pos + } + + /// Get the length of the current OBU in AnnexB format. + pub fn current_annexb_obu_length( + &mut self, + annexb_state: &mut AnnexBState, + ) -> Result, String> { + if !self.more_data_in_bitstream() { + return Ok(None); + } + + #[allow(clippy::comparison_chain)] + if annexb_state.temporal_unit_consumed == annexb_state.temporal_unit_size { + annexb_state.temporal_unit_size = 0; + } else if annexb_state.temporal_unit_consumed > annexb_state.temporal_unit_size { + return Err(format!( + "temporal_unit_size is {} but we consumed {} bytes", + annexb_state.temporal_unit_size, annexb_state.temporal_unit_consumed, + )); + } + + if annexb_state.temporal_unit_size == 0 { + annexb_state.temporal_unit_size = self.read_leb128()?; + if annexb_state.temporal_unit_size == 0 { + return Ok(None); + } + } + + let start_pos = self.consumed(0); + + #[allow(clippy::comparison_chain)] + if annexb_state.frame_unit_consumed == annexb_state.frame_unit_size { + annexb_state.frame_unit_size = 0; + } else if annexb_state.frame_unit_consumed > annexb_state.frame_unit_size { + return Err(format!( + "frame_unit_size is {} but we consumed {} bytes", + annexb_state.frame_unit_size, annexb_state.frame_unit_consumed, + )); + } + + if annexb_state.frame_unit_size == 0 { + annexb_state.frame_unit_size = self.read_leb128()?; + if annexb_state.frame_unit_size == 0 { + return Ok(None); + } + annexb_state.temporal_unit_consumed += self.consumed(start_pos); + } + + let start_pos = self.consumed(0); + let obu_length = self.read_leb128()?; + let consumed = self.consumed(start_pos); + + annexb_state.temporal_unit_consumed += consumed; + annexb_state.frame_unit_consumed += consumed; + + Ok(Some(obu_length.try_into().unwrap())) + } + + /// Implements 5.3.4. + pub fn read_trailing_bits(&mut self, mut num_bits: u64) -> Result<(), String> { + let trailing_one_bit = self.0.read_bit()?; + num_bits -= 1; + + if !trailing_one_bit { + return Err("bad padding: trailing_one_bit is not set".into()); + } + + while num_bits > 0 { + let trailing_zero_bit = self.0.read_bit()?; + if trailing_zero_bit { + return Err("bad padding: trailing_zero_bit is set".into()); + } + num_bits -= 1; + } + + Ok(()) + } + + fn decode_subexp(&mut self, 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 { + let num_bits = num_syms - mk; + let subexp_final_bits = self.read_ns(num_bits as usize)?; + return Ok(subexp_final_bits); + } else { + let subexp_more_bits = self.0.read_bit()?; + if subexp_more_bits { + i += 1; + mk += a; + } else { + let num_bits = b2 as usize; + let subexp_bits = self.0.read_bits::(num_bits)?; + return Ok(subexp_bits + mk as u32); + } + } + } + } + + /// Implements 5.9.27. + pub fn decode_unsigned_subexp_with_ref(&mut self, mx: i32, r: i32) -> Result { + let v = self.decode_subexp(mx)?; + if (r << 1) <= mx { + Ok(helpers::inverse_recenter(r, v.try_into().unwrap()).try_into().unwrap()) + } else { + let res = mx - 1 - helpers::inverse_recenter(mx - 1 - r, v.try_into().unwrap()); + Ok(res.try_into().unwrap()) + } + } + + /// Implements 5.9.26. + pub fn decode_signed_subexp_with_ref( + &mut self, + low: i32, + high: i32, + r: i32, + ) -> Result { + let x = self.decode_unsigned_subexp_with_ref(high - low, r - low)?; + Ok(i32::try_from(x).unwrap() + low) + } + + /// Implements 5.3.5 Byte alignment syntax + pub fn byte_alignment(&mut self) -> Result<(), String> { + while (self.0.position() & 7) != 0 { + self.0.read_bit()?; + } + + Ok(()) + } +} + +impl<'a> Clone for Reader<'a> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} diff --git a/vendor/cros-codecs/src/codec/av1/synthesizer.rs b/vendor/cros-codecs/src/codec/av1/synthesizer.rs new file mode 100644 index 00000000..ba5bf107 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/synthesizer.rs @@ -0,0 +1,1796 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::fmt; +use std::io::Write; +use std::num::TryFromIntError; + +use crate::codec::av1::helpers::clip3; +use crate::codec::av1::parser::BitDepth; +use crate::codec::av1::parser::ChromaSamplePosition; +use crate::codec::av1::parser::ColorPrimaries; +use crate::codec::av1::parser::FrameHeaderObu; +use crate::codec::av1::parser::FrameRestorationType; +use crate::codec::av1::parser::FrameType; +use crate::codec::av1::parser::InterpolationFilter; +use crate::codec::av1::parser::MatrixCoefficients; +use crate::codec::av1::parser::ObuHeader; +use crate::codec::av1::parser::ObuType; +use crate::codec::av1::parser::Profile; +use crate::codec::av1::parser::ReferenceFrameType; +use crate::codec::av1::parser::SequenceHeaderObu; +use crate::codec::av1::parser::TemporalDelimiterObu; +use crate::codec::av1::parser::TransferCharacteristics; +use crate::codec::av1::parser::TxMode; +use crate::codec::av1::parser::WarpModelType; +use crate::codec::av1::parser::FEATURE_BITS; +use crate::codec::av1::parser::FEATURE_MAX; +use crate::codec::av1::parser::FEATURE_SIGNED; +use crate::codec::av1::parser::MAX_NUM_OPERATING_POINTS; +use crate::codec::av1::parser::MAX_NUM_PLANES; +use crate::codec::av1::parser::MAX_SEGMENTS; +use crate::codec::av1::parser::NUM_REF_FRAMES; +use crate::codec::av1::parser::PRIMARY_REF_NONE; +use crate::codec::av1::parser::REFS_PER_FRAME; +use crate::codec::av1::parser::SEG_LVL_MAX; +use crate::codec::av1::parser::SELECT_INTEGER_MV; +use crate::codec::av1::parser::SELECT_SCREEN_CONTENT_TOOLS; +use crate::codec::av1::parser::SUPERRES_DENOM_BITS; +use crate::codec::av1::parser::SUPERRES_DENOM_MIN; +use crate::codec::av1::parser::SUPERRES_NUM; +use crate::codec::av1::parser::TOTAL_REFS_PER_FRAME; +use crate::codec::av1::writer::ObuWriter; +use crate::codec::av1::writer::ObuWriterError; + +mod private { + pub trait ObuStruct {} +} + +impl private::ObuStruct for SequenceHeaderObu {} + +impl private::ObuStruct for TemporalDelimiterObu {} + +impl private::ObuStruct for FrameHeaderObu {} + +#[derive(Debug)] +pub enum SynthesizerError { + Unsupported, + InvalidSyntaxElementValue(&'static str), + ConversionError(TryFromIntError), + ObuWriter(ObuWriterError), + Io(std::io::Error), +} + +impl fmt::Display for SynthesizerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SynthesizerError::Unsupported => write!(f, "tried to synthesize unsupported settings"), + SynthesizerError::InvalidSyntaxElementValue(x) => { + write!(f, "invalid syntax element for value {}", x) + } + SynthesizerError::ConversionError(x) => write!(f, "{}", x.to_string()), + SynthesizerError::ObuWriter(x) => write!(f, "{}", x.to_string()), + SynthesizerError::Io(x) => write!(f, "{}", x.to_string()), + } + } +} + +impl From for SynthesizerError { + fn from(err: TryFromIntError) -> Self { + SynthesizerError::ConversionError(err) + } +} + +impl From for SynthesizerError { + fn from(err: ObuWriterError) -> Self { + SynthesizerError::ObuWriter(err) + } +} + +impl From for SynthesizerError { + fn from(err: std::io::Error) -> Self { + SynthesizerError::Io(err) + } +} + +pub type SynthesizerResult = Result; + +pub struct Synthesizer<'o, O: private::ObuStruct, W: Write> { + writer: ObuWriter, + obu: &'o O, +} + +impl<'o, O, W> Synthesizer<'o, O, W> +where + O: private::ObuStruct, + W: Write, +{ + fn new(writer: W, obu: &'o O) -> Self { + Self { writer: ObuWriter::new(writer), obu } + } + + fn f>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> { + let value: u32 = value.into(); + self.writer.write_f(bits, value)?; + Ok(()) + } + + fn leb128>(&mut self, value: T) -> SynthesizerResult<()> { + let value: u32 = value.into(); + self.writer.write_leb128(value, 0)?; + Ok(()) + } + + fn uvlc>(&mut self, value: T) -> SynthesizerResult<()> { + self.writer.write_uvlc(value)?; + Ok(()) + } + + fn su>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> { + self.writer.write_su(bits, value)?; + Ok(()) + } + + #[cfg(any(test, debug_assertions))] + fn invalid_element_value(&mut self, element: &'static str) -> SynthesizerResult<()> { + Err(SynthesizerError::InvalidSyntaxElementValue(element)) + } + + #[cfg(not(any(test, debug_assertions)))] + fn invalid_element_value(&mut self, element: &'static str) -> SynthesizerResult<()> { + log::error!("Invalid syntax element value: '{element}', expect corrupted bitstream"); + Ok(()) + } + + /// Writes 5.3.2. OBU header syntax + fn obu_header(&mut self, obu: &ObuHeader) -> SynthesizerResult<()> { + self.f(1, /* obu_forbidden_bit */ 0u32)?; + self.f(4, obu.obu_type as u32)?; + self.f(1, obu.extension_flag)?; + self.f(1, obu.has_size_field)?; + self.f(1, /* obu_reserved_1bit */ 0u32)?; + + if obu.extension_flag { + self.obu_extension_header(obu)?; + } + + Ok(()) + } + + /// Writes AV1 5.3.3. OBU extension header syntax + fn obu_extension_header(&mut self, obu: &ObuHeader) -> SynthesizerResult<()> { + // AV1 5.3.3 + self.f(3, obu.temporal_id)?; + self.f(2, obu.spatial_id)?; + self.f(3, /* extension_header_reserved_3bits */ 0u32)?; + + Ok(()) + } + + fn obu_size(&mut self, size: u32) -> SynthesizerResult<()> { + self.leb128(size) + } + + /// Writes AV1 5.3.4. Trailing bits syntax + fn trailing_bits(&mut self) -> SynthesizerResult<()> { + self.f(1, /* trailing_one_bit */ 1u32)?; + while !self.writer.aligned() { + self.f(1, /* trailing_zero_bit */ 0u32)?; + } + + Ok(()) + } +} + +impl<'o, W> Synthesizer<'o, TemporalDelimiterObu, W> +where + W: Write, +{ + pub fn synthesize(obu: &'o TemporalDelimiterObu, writer: W) -> SynthesizerResult<()> { + let mut s = Self::new(writer, obu); + + if obu.obu_header.obu_type != ObuType::TemporalDelimiter { + s.invalid_element_value("obu_type")?; + } + + s.obu_header(&obu.obu_header)?; + + if obu.obu_header.has_size_field { + s.obu_size(0u32)?; + } + + Ok(()) + } +} + +impl<'o, W> Synthesizer<'o, SequenceHeaderObu, W> +where + W: Write, +{ + pub fn synthesize(obu: &'o SequenceHeaderObu, mut writer: W) -> SynthesizerResult<()> { + let mut s = Synthesizer::new(&mut writer, obu); + + if obu.obu_header.obu_type != ObuType::SequenceHeader { + s.invalid_element_value("obu_type")?; + } + + s.obu_header(&obu.obu_header)?; + + if !obu.obu_header.has_size_field { + s.sequence_header_obu()?; + s.trailing_bits()?; + return Ok(()); + } + + let mut buf = Vec::::new(); + let mut buffered = Synthesizer::new(&mut buf, obu); + buffered.sequence_header_obu()?; + buffered.trailing_bits()?; + drop(buffered); + + s.obu_size(buf.len() as u32)?; + drop(s); + + writer.write_all(&buf)?; + + Ok(()) + } + + /// Writes AV1 5.5.1. General sequence header OBU syntax + fn sequence_header_obu(&mut self) -> SynthesizerResult<()> { + self.f(3, self.obu.seq_profile as u32)?; + self.f(1, self.obu.still_picture)?; + self.f(1, self.obu.reduced_still_picture_header)?; + + if self.obu.reduced_still_picture_header { + if self.obu.timing_info_present_flag { + self.invalid_element_value("reduced_still_picture_header")? + } + if self.obu.timing_info_present_flag { + self.invalid_element_value("timing_info_present_flag")? + } + if self.obu.initial_display_delay_present_flag { + self.invalid_element_value("initial_display_delay_present_flag")? + } + if self.obu.operating_points_cnt_minus_1 != 0 { + self.invalid_element_value("operating_points_cnt_minus_1")? + } + if self.obu.operating_points[0].idc != 0 { + self.invalid_element_value("operating_point_idc")? + } + + self.f(5, self.obu.operating_points[0].seq_level_idx)?; + + if self.obu.operating_points[0].decoder_model_present_for_this_op { + self.invalid_element_value("decoder_model_present_for_this_op")? + } + if self.obu.operating_points[0].initial_display_delay_present_for_this_op { + self.invalid_element_value("initial_display_delay_present_for_this_op")? + } + } else { + self.f(1, self.obu.timing_info_present_flag)?; + if self.obu.timing_info_present_flag { + self.timing_info()?; + self.f(1, self.obu.decoder_model_info_present_flag)?; + if self.obu.decoder_model_info_present_flag { + self.decoder_model_info()?; + } + } else if self.obu.decoder_model_info_present_flag { + self.invalid_element_value("decoder_model_info_present_flag")?; + } + + self.f(1, self.obu.initial_display_delay_present_flag)?; + + if self.obu.operating_points_cnt_minus_1 > MAX_NUM_OPERATING_POINTS as u32 { + self.invalid_element_value("operating_points_cnt_minus_1")?; + } + self.f(5, self.obu.operating_points_cnt_minus_1)?; + for i in 0..=self.obu.operating_points_cnt_minus_1 { + let op = &self.obu.operating_points[i as usize]; + + self.f(12, op.idc)?; + self.f(5, op.seq_level_idx)?; + if op.seq_level_idx > 7 { + self.f(1, op.seq_tier)?; + } else if op.seq_tier != 0 { + self.invalid_element_value("seq_tier")?; + } + + if self.obu.decoder_model_info_present_flag { + self.f(1, op.decoder_model_present_for_this_op)?; + if op.decoder_model_present_for_this_op { + self.operating_parameters_info(i as usize)?; + } + } else if op.decoder_model_present_for_this_op { + self.invalid_element_value("decoder_model_present_for_this_op")?; + } + + if self.obu.initial_display_delay_present_flag { + self.f(1, op.initial_display_delay_present_for_this_op)?; + if op.initial_display_delay_present_for_this_op { + self.f(4, op.initial_display_delay_minus_1)?; + } + } + } + } + + let bits = u16::BITS - self.obu.max_frame_width_minus_1.leading_zeros(); + if self.obu.frame_width_bits_minus_1 as u32 + 1 < bits { + self.invalid_element_value("frame_width_bits_minus_1")?; + } + + let bits = u16::BITS - self.obu.max_frame_height_minus_1.leading_zeros(); + if self.obu.frame_height_bits_minus_1 as u32 + 1 < bits { + self.invalid_element_value("frame_height_bits_minus_1")?; + } + + self.f(4, self.obu.frame_width_bits_minus_1)?; + self.f(4, self.obu.frame_height_bits_minus_1)?; + + let n = self.obu.frame_width_bits_minus_1 as usize + 1; + if (n as u32) < u16::BITS - self.obu.max_frame_width_minus_1.leading_zeros() { + self.invalid_element_value("max_frame_width_minus_1")?; + } + self.f(n, self.obu.max_frame_width_minus_1)?; + + let n = self.obu.frame_height_bits_minus_1 as usize + 1; + if (n as u32) < u16::BITS - self.obu.max_frame_height_minus_1.leading_zeros() { + self.invalid_element_value("max_frame_height_minus_1")?; + } + self.f(n, self.obu.max_frame_height_minus_1)?; + + if self.obu.reduced_still_picture_header { + if self.obu.frame_id_numbers_present_flag { + self.invalid_element_value("frame_id_numbers_present_flag")?; + } + } else { + self.f(1, self.obu.frame_id_numbers_present_flag)?; + } + + if self.obu.frame_id_numbers_present_flag { + self.f(4, self.obu.delta_frame_id_length_minus_2)?; + self.f(3, self.obu.additional_frame_id_length_minus_1)?; + } + + self.f(1, self.obu.use_128x128_superblock)?; + self.f(1, self.obu.enable_filter_intra)?; + self.f(1, self.obu.enable_intra_edge_filter)?; + + if self.obu.reduced_still_picture_header { + if self.obu.enable_interintra_compound { + self.invalid_element_value("enable_interintra_compound")?; + } + if self.obu.enable_masked_compound { + self.invalid_element_value("enable_masked_compound")?; + } + if self.obu.enable_warped_motion { + self.invalid_element_value("enable_warped_motion")?; + } + if self.obu.enable_dual_filter { + self.invalid_element_value("enable_dual_filter")?; + } + if self.obu.enable_order_hint { + self.invalid_element_value("enable_order_hint")?; + } + if self.obu.enable_jnt_comp { + self.invalid_element_value("enable_jnt_comp")?; + } + if self.obu.enable_ref_frame_mvs { + self.invalid_element_value("enable_ref_frame_mvs")?; + } + if self.obu.seq_force_screen_content_tools != SELECT_SCREEN_CONTENT_TOOLS as u32 { + self.invalid_element_value("seq_force_screen_content_tools")?; + } + if self.obu.seq_force_integer_mv != SELECT_INTEGER_MV as u32 { + self.invalid_element_value("seq_force_integer_mv")?; + } + if self.obu.order_hint_bits != 0 { + self.invalid_element_value("OrderHintBits")?; + } + } else { + self.f(1, self.obu.enable_interintra_compound)?; + self.f(1, self.obu.enable_masked_compound)?; + self.f(1, self.obu.enable_warped_motion)?; + self.f(1, self.obu.enable_dual_filter)?; + self.f(1, self.obu.enable_order_hint)?; + + if self.obu.enable_order_hint { + self.f(1, self.obu.enable_jnt_comp)?; + self.f(1, self.obu.enable_ref_frame_mvs)?; + } else { + if self.obu.enable_jnt_comp { + self.invalid_element_value("enable_jnt_comp")?; + } + if self.obu.enable_ref_frame_mvs { + self.invalid_element_value("enable_ref_frame_mvs")?; + } + } + + self.f(1, self.obu.seq_choose_screen_content_tools)?; + if self.obu.seq_choose_screen_content_tools { + if self.obu.seq_force_screen_content_tools != SELECT_SCREEN_CONTENT_TOOLS as u32 { + self.invalid_element_value("seq_force_screen_content_tools")?; + } + } else { + self.f(1, self.obu.seq_force_screen_content_tools)?; + } + + if self.obu.seq_force_screen_content_tools > 0 { + self.f(1, self.obu.seq_choose_integer_mv)?; + if self.obu.seq_choose_integer_mv { + if self.obu.seq_force_integer_mv != SELECT_INTEGER_MV as u32 { + self.invalid_element_value("seq_force_integer_mv")?; + } + } else { + self.f(1, self.obu.seq_force_integer_mv)?; + } + } else if self.obu.seq_force_integer_mv != SELECT_INTEGER_MV as u32 { + self.invalid_element_value("seq_force_integer_mv")?; + } + + if self.obu.enable_order_hint { + self.f(3, self.obu.order_hint_bits_minus_1 as u32)?; + if self.obu.order_hint_bits != self.obu.order_hint_bits_minus_1 + 1 { + self.invalid_element_value("OrderHintBits")?; + } + } else if self.obu.order_hint_bits != 0 { + self.invalid_element_value("OrderHintBits")?; + } + } + + self.f(1, self.obu.enable_superres)?; + self.f(1, self.obu.enable_cdef)?; + self.f(1, self.obu.enable_restoration)?; + self.color_config()?; + self.f(1, self.obu.film_grain_params_present)?; + + Ok(()) + } + + /// Writes AV1 5.5.2. Color config syntax + fn color_config(&mut self) -> SynthesizerResult<()> { + let cc = &self.obu.color_config; + + self.f(1, cc.high_bitdepth)?; + if matches!(self.obu.seq_profile, Profile::Profile2) && cc.high_bitdepth { + self.f(1, cc.twelve_bit)?; + + if (cc.twelve_bit && self.obu.bit_depth != BitDepth::Depth12) + || (!cc.twelve_bit && self.obu.bit_depth != BitDepth::Depth10) + { + self.invalid_element_value("BitDepth")?; + } + } else if self.obu.seq_profile <= Profile::Profile2 + && ((cc.high_bitdepth && self.obu.bit_depth != BitDepth::Depth10) + || (!cc.high_bitdepth && self.obu.bit_depth != BitDepth::Depth8)) + { + self.invalid_element_value("BitDepth")?; + } + + if matches!(self.obu.seq_profile, Profile::Profile1) { + if cc.mono_chrome { + self.invalid_element_value("mono_chrome")?; + } + } else { + self.f(1, cc.mono_chrome)?; + } + + if (cc.mono_chrome && self.obu.num_planes != 1) + || (!cc.mono_chrome && self.obu.num_planes != 3) + { + self.invalid_element_value("NumPlanes")?; + } + + self.f(1, cc.color_description_present_flag)?; + if cc.color_description_present_flag { + self.f(8, cc.color_primaries as u32)?; + self.f(8, cc.transfer_characteristics as u32)?; + self.f(8, cc.matrix_coefficients as u32)?; + } else { + if !matches!(cc.color_primaries, ColorPrimaries::Unspecified) { + self.invalid_element_value("color_primaries")?; + } + if !matches!(cc.transfer_characteristics, TransferCharacteristics::Unspecified) { + self.invalid_element_value("transfer_characteristics")?; + } + if !matches!(cc.matrix_coefficients, MatrixCoefficients::Unspecified) { + self.invalid_element_value("matrix_coefficients")?; + } + } + + if cc.mono_chrome { + self.f(1, cc.color_range)?; + + if !cc.subsampling_x { + self.invalid_element_value("subsampling_x")?; + } + if !cc.subsampling_y { + self.invalid_element_value("subsampling_y")?; + } + if !matches!(cc.chroma_sample_position, ChromaSamplePosition::Unknown) { + self.invalid_element_value("chroma_sample_position")?; + } + if !matches!(cc.chroma_sample_position, ChromaSamplePosition::Unknown) { + self.invalid_element_value("chroma_sample_position")?; + } + if cc.separate_uv_delta_q { + self.invalid_element_value("separate_uv_delta_q")?; + } + + return Ok(()); + } else if matches!(cc.color_primaries, ColorPrimaries::Bt709) + && matches!(cc.transfer_characteristics, TransferCharacteristics::Srgb) + && matches!(cc.matrix_coefficients, MatrixCoefficients::Identity) + { + if !cc.color_range { + self.invalid_element_value("color_range")?; + } + if cc.subsampling_x { + self.invalid_element_value("subsampling_x")?; + } + if cc.subsampling_y { + self.invalid_element_value("subsampling_y")?; + } + } else { + self.f(1, cc.color_range)?; + + match self.obu.seq_profile { + Profile::Profile0 => { + if !cc.subsampling_x { + self.invalid_element_value("subsampling_x")?; + } + if !cc.subsampling_y { + self.invalid_element_value("subsampling_y")?; + } + } + Profile::Profile1 => { + if cc.subsampling_x { + self.invalid_element_value("subsampling_x")?; + } + if cc.subsampling_y { + self.invalid_element_value("subsampling_y")?; + } + } + _ => { + if matches!(self.obu.bit_depth, BitDepth::Depth12) { + self.f(1, cc.subsampling_x)?; + if cc.subsampling_x { + self.f(1, cc.subsampling_y)?; + } else if cc.subsampling_y { + self.invalid_element_value("subsampling_y")?; + } + } else { + if !cc.subsampling_x { + self.invalid_element_value("subsampling_x")?; + } + if cc.subsampling_y { + self.invalid_element_value("subsampling_y")?; + } + } + } + } + + if cc.subsampling_x && cc.subsampling_y { + self.f(2, cc.chroma_sample_position as u32)?; + } + } + + self.f(1, cc.separate_uv_delta_q)?; + + Ok(()) + } + + /// Writes AV1 5.5.3. Timing info syntax + fn timing_info(&mut self) -> SynthesizerResult<()> { + // AV1 5.5.3 + let ti = &self.obu.timing_info; + + self.f(32, ti.num_units_in_display_tick)?; + self.f(32, ti.time_scale)?; + self.f(1, ti.equal_picture_interval)?; + if ti.equal_picture_interval { + self.uvlc(ti.num_ticks_per_picture_minus_1)?; + } + + Ok(()) + } + + /// Writes AV1 5.5.4. Decoder model info syntax + fn decoder_model_info(&mut self) -> SynthesizerResult<()> { + let dm = &self.obu.decoder_model_info; + + self.f(5, dm.buffer_delay_length_minus_1)?; + self.f(32, dm.num_units_in_decoding_tick)?; + self.f(5, dm.buffer_removal_time_length_minus_1)?; + self.f(5, dm.frame_presentation_time_length_minus_1)?; + + Ok(()) + } + + /// Writes AV1 5.5.5. Operating parameters info syntax + fn operating_parameters_info(&mut self, i: usize) -> SynthesizerResult<()> { + let op = &self.obu.operating_points[i]; + + let n = usize::from(self.obu.decoder_model_info.buffer_delay_length_minus_1) + 1; + self.f(n, op.decoder_buffer_delay)?; + self.f(n, op.encoder_buffer_delay)?; + self.f(1, op.low_delay_mode_flag)?; + + Ok(()) + } +} + +impl<'o, W> Synthesizer<'o, FrameHeaderObu, W> +where + W: Write, +{ + pub fn synthesize( + obu: &'o FrameHeaderObu, + sequence: &'o SequenceHeaderObu, + mut writer: W, + ) -> SynthesizerResult<()> { + let mut s = Synthesizer::new(&mut writer, obu); + + if obu.obu_header.obu_type != ObuType::FrameHeader { + s.invalid_element_value("obu_type")?; + } + + s.obu_header(&obu.obu_header)?; + + if !obu.obu_header.has_size_field { + s.frame_header_obu(sequence)?; + s.trailing_bits()?; + return Ok(()); + } + + let mut buf = Vec::::new(); + let mut buffered = Synthesizer::new(&mut buf, obu); + buffered.frame_header_obu(sequence)?; + buffered.trailing_bits()?; + drop(buffered); + + s.obu_size(buf.len() as u32)?; + drop(s); + + writer.write_all(&buf)?; + + Ok(()) + } + + /// Writes AV1 5.9.1. General frame header OBU syntax + fn frame_header_obu(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + self.uncompressed_header(sequence) + } + + /// Writes AV1 5.9.2. Uncompressed header syntax + fn uncompressed_header(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + const ALL_FRAMES: u32 = (1 << NUM_REF_FRAMES) - 1; + + // idLen + let id_len = usize::try_from( + sequence.additional_frame_id_length_minus_1 + + sequence.delta_frame_id_length_minus_2 + + 3, + )?; + + if sequence.reduced_still_picture_header { + if !self.obu.show_existing_frame { + self.invalid_element_value("show_existing_frame")?; + } + if !matches!(self.obu.frame_type, FrameType::KeyFrame) { + self.invalid_element_value("frame_type")?; + } + if !self.obu.show_frame { + self.invalid_element_value("show_frame")?; + } + if !self.obu.showable_frame { + self.invalid_element_value("showable_frame")?; + } + if !self.obu.frame_is_intra { + self.invalid_element_value("FrameIsIntra")?; + } + } else { + self.f(1, self.obu.show_existing_frame)?; + if self.obu.show_existing_frame { + self.f(3, self.obu.frame_to_show_map_idx)?; + + if sequence.decoder_model_info_present_flag + && !sequence.timing_info.equal_picture_interval + { + self.temporal_point_info(sequence)?; + } + + if sequence.frame_id_numbers_present_flag { + self.f(id_len, self.obu.display_frame_id)?; + } + return Ok(()); + } + + self.f(2, self.obu.frame_type as u32)?; + if self.obu.frame_is_intra + ^ matches!(self.obu.frame_type, FrameType::IntraOnlyFrame | FrameType::KeyFrame) + { + self.invalid_element_value("FrameIsIntra")?; + } + + self.f(1, self.obu.show_frame)?; + if self.obu.show_frame + && sequence.decoder_model_info_present_flag + && !sequence.timing_info.equal_picture_interval + { + self.temporal_point_info(sequence)?; + } + + if self.obu.show_frame { + if self.obu.showable_frame ^ !matches!(self.obu.frame_type, FrameType::KeyFrame) { + self.invalid_element_value("showable_frame")?; + } + } else { + self.f(1, self.obu.showable_frame)?; + } + + if matches!(self.obu.frame_type, FrameType::SwitchFrame) + || (matches!(self.obu.frame_type, FrameType::KeyFrame) && self.obu.show_frame) + { + if !self.obu.error_resilient_mode { + self.invalid_element_value("error_resilient_mode")?; + } + } else { + self.f(1, self.obu.error_resilient_mode)?; + } + } + + self.f(1, self.obu.disable_cdf_update)?; + if sequence.seq_force_screen_content_tools == SELECT_SCREEN_CONTENT_TOOLS as u32 { + self.f(1, self.obu.allow_screen_content_tools)?; + } else if self.obu.allow_screen_content_tools != sequence.seq_force_screen_content_tools { + self.invalid_element_value("allow_screen_content_tools")?; + } + + if self.obu.allow_screen_content_tools != 0 { + if sequence.seq_force_integer_mv == SELECT_INTEGER_MV as u32 { + self.f(1, self.obu.force_integer_mv)?; + } else if self.obu.force_integer_mv != sequence.seq_force_integer_mv { + self.invalid_element_value("force_integer_mv")?; + } + } else if self.obu.force_integer_mv != 0 + && !(self.obu.frame_is_intra && self.obu.force_integer_mv != 1) + { + self.invalid_element_value("force_integer_mv")?; + } + + if sequence.frame_id_numbers_present_flag { + self.f(id_len, self.obu.current_frame_id)?; + } else if self.obu.current_frame_id != 0 { + self.invalid_element_value("current_frame_id")?; + } + + if matches!(self.obu.frame_type, FrameType::SwitchFrame) { + if !self.obu.frame_size_override_flag { + self.invalid_element_value("frame_size_override_flag")?; + } + } else if sequence.reduced_still_picture_header { + if self.obu.frame_size_override_flag { + self.invalid_element_value("frame_size_override_flag")?; + } + } else { + self.f(1, self.obu.frame_size_override_flag)?; + } + + if sequence.order_hint_bits != 0 { + if sequence.order_hint_bits != sequence.order_hint_bits_minus_1 + 1 { + self.invalid_element_value("order_hint_bits_minus_1")?; + } + + self.f(sequence.order_hint_bits as usize, self.obu.order_hint)?; + } + + if self.obu.frame_is_intra || self.obu.error_resilient_mode { + if self.obu.primary_ref_frame != PRIMARY_REF_NONE { + self.invalid_element_value("primary_ref_frame")?; + } + } else { + self.f(3, self.obu.primary_ref_frame)?; + } + + if sequence.decoder_model_info_present_flag { + self.f(1, self.obu.buffer_removal_time_present_flag)?; + + for op_num in 0..=sequence.operating_points_cnt_minus_1 { + let op = &sequence.operating_points[op_num as usize]; + if op.decoder_model_present_for_this_op { + let in_temporal_layer = (op.idc >> self.obu.obu_header.temporal_id) & 1 != 0; + let in_spatial_layer = + (op.idc >> (self.obu.obu_header.spatial_id + 8)) & 1 != 0; + + if op.idc == 0 || (in_temporal_layer && in_spatial_layer) { + let n = usize::from( + sequence.decoder_model_info.buffer_removal_time_length_minus_1 + 1, + ); + + self.f(n, op.decoder_buffer_delay)?; + } + } + } + } + + if matches!(self.obu.frame_type, FrameType::SwitchFrame) + || (matches!(self.obu.frame_type, FrameType::KeyFrame) && self.obu.show_frame) + { + if self.obu.refresh_frame_flags != ALL_FRAMES { + self.invalid_element_value("refresh_frame_flags")?; + } + } else { + self.f(8, self.obu.refresh_frame_flags)?; + } + + if (!self.obu.frame_is_intra || self.obu.refresh_frame_flags != ALL_FRAMES) + && self.obu.error_resilient_mode + && sequence.enable_order_hint + { + for i in 0..NUM_REF_FRAMES { + self.f(sequence.order_hint_bits as usize, self.obu.ref_order_hint[i])?; + } + } + + if self.obu.frame_is_intra { + self.frame_size(sequence)?; + self.render_size()?; + + if self.obu.allow_screen_content_tools != 0 + && self.obu.upscaled_width == self.obu.frame_width + { + self.f(1, self.obu.allow_intrabc)?; + } + } else { + if !sequence.enable_order_hint { + if self.obu.frame_refs_short_signaling { + self.invalid_element_value("frame_refs_short_signaling")?; + } + } else { + self.f(1, self.obu.frame_refs_short_signaling)?; + if self.obu.frame_refs_short_signaling { + self.f(3, self.obu.last_frame_idx)?; + self.f(3, self.obu.gold_frame_idx)?; + } + } + + for i in 0..REFS_PER_FRAME { + let ref_frame_idx = self.obu.ref_frame_idx[i] as u32; + if !self.obu.frame_refs_short_signaling { + self.f(3, ref_frame_idx)?; + } + + if sequence.frame_id_numbers_present_flag { + let n = usize::try_from(sequence.delta_frame_id_length_minus_2 + 2)?; + + let delta_frame_id_minus_1 = ((self.obu.current_frame_id - ref_frame_idx + + (1 << id_len)) + % (1 << id_len)) + - 1; + + self.f(n, delta_frame_id_minus_1)?; + } + } + + if self.obu.frame_size_override_flag && !self.obu.error_resilient_mode { + self.frame_size_with_refs()?; + } else { + self.frame_size(sequence)?; + self.render_size()?; + } + + if self.obu.force_integer_mv != 0 { + if !self.obu.allow_high_precision_mv { + self.invalid_element_value("allow_high_precision_mv")?; + } + } else { + self.f(1, self.obu.allow_high_precision_mv)?; + } + + self.read_interpolation_filter()?; + self.f(1, self.obu.is_motion_mode_switchable)?; + + if self.obu.error_resilient_mode || !sequence.enable_ref_frame_mvs { + if self.obu.use_ref_frame_mvs { + self.invalid_element_value("use_ref_frame_mvs")?; + } + } else { + self.f(1, self.obu.use_ref_frame_mvs)?; + } + } + + if sequence.reduced_still_picture_header || self.obu.disable_cdf_update { + if !self.obu.disable_frame_end_update_cdf { + self.invalid_element_value("disable_frame_end_update_cdf")? + } + } else { + self.f(1, self.obu.disable_frame_end_update_cdf)?; + } + + self.tile_info()?; + self.quantization_params(sequence)?; + self.segmentation_params()?; + + self.delta_q_params()?; + self.delta_lf_params()?; + + if self.obu.coded_lossless && self.obu.lossless_array != [true; MAX_SEGMENTS] { + self.invalid_element_value("CodedLossless")?; + } + + if self.obu.all_lossless + ^ (self.obu.coded_lossless && self.obu.frame_width == self.obu.upscaled_width) + { + self.invalid_element_value("AllLossless")?; + } + + self.loop_filter_params(sequence)?; + self.cdef_params(sequence)?; + self.lr_params(sequence)?; + self.read_tx_mode()?; + self.frame_reference_mode()?; + self.skip_mode_params()?; + + if self.obu.frame_is_intra + || self.obu.error_resilient_mode + || !sequence.enable_warped_motion + { + if self.obu.allow_warped_motion { + self.invalid_element_value("allow_warped_motion")?; + } + } else { + self.f(1, self.obu.allow_warped_motion)?; + } + + self.f(1, self.obu.reduced_tx_set)?; + + self.global_motion_params()?; + self.film_grain_params(sequence)?; + + Ok(()) + } + + /// Writes AV1 5.9.31. Temporal point info syntax + fn temporal_point_info(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + let n = usize::try_from( + sequence.decoder_model_info.frame_presentation_time_length_minus_1 + 1, + )?; + + self.f(n, self.obu.frame_presentation_time)?; + + Ok(()) + } + + /// Writes AV1 5.9.5. Frame size syntax + fn frame_size(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + if self.obu.frame_size_override_flag { + let n = sequence.frame_width_bits_minus_1 as usize + 1; + self.f(n, self.obu.frame_width - 1)?; + let n = sequence.frame_height_bits_minus_1 as usize + 1; + self.f(n, self.obu.frame_height - 1)?; + } else { + if self.obu.frame_width != sequence.max_frame_width_minus_1 as u32 + 1 { + self.invalid_element_value("FrameWidth")?; + } + if self.obu.frame_height != sequence.max_frame_height_minus_1 as u32 + 1 { + self.invalid_element_value("FrameHeight")?; + } + + self.superres_params(sequence)?; + } + + Ok(()) + } + + /// Writes AV1 5.9.6. Render size syntax + fn render_size(&mut self) -> SynthesizerResult<()> { + self.f(1, self.obu.render_and_frame_size_different)?; + + if self.obu.render_and_frame_size_different { + self.f(16, self.obu.render_width - 1)?; + self.f(16, self.obu.render_height - 1)?; + } else { + if self.obu.render_width != self.obu.upscaled_width { + self.invalid_element_value("RenderWidth")?; + } + if self.obu.render_height != self.obu.frame_height { + self.invalid_element_value("RenderHeight")?; + } + } + + Ok(()) + } + + /// Writes AV1 5.9.7. Frame size with refs syntax + fn frame_size_with_refs(&mut self) -> SynthesizerResult<()> { + log::error!("Syntax element frame_size_with_refs is unsupported"); + Err(SynthesizerError::Unsupported) + } + + /// Writes AV1 5.9.8. Superres params syntax + fn superres_params(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + if sequence.enable_superres { + self.f(1, self.obu.use_superres)?; + } else if self.obu.use_superres { + self.invalid_element_value("use_superres")?; + } + + if self.obu.use_superres { + let coded_denom = self.obu.superres_denom - SUPERRES_DENOM_MIN as u32; + + self.f(SUPERRES_DENOM_BITS, coded_denom)?; + } else if self.obu.superres_denom != SUPERRES_NUM as u32 { + self.invalid_element_value("superres_denom")?; + } + + Ok(()) + } + + /// Writes AV1 5.9.10. Interpolation filter syntax + fn read_interpolation_filter(&mut self) -> SynthesizerResult<()> { + self.f(1, self.obu.is_filter_switchable)?; + if self.obu.is_filter_switchable { + if !matches!(self.obu.interpolation_filter, InterpolationFilter::Switchable) { + self.invalid_element_value("interpolation_filter")?; + } + } else { + self.f(2, self.obu.interpolation_filter as u32)?; + } + + Ok(()) + } + + /// Writes AV1 5.9.11. Loop filter params syntax + fn loop_filter_params(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + if self.obu.coded_lossless || self.obu.allow_intrabc { + if !matches!(self.obu.loop_filter_params.loop_filter_level, [0, 0, _, _]) { + self.invalid_element_value("loop_filter_level")?; + } + if self.obu.loop_filter_params.loop_filter_ref_deltas != [1, 0, 0, 0, 0, -1, -1, -1] { + self.invalid_element_value("loop_filter_ref_deltas")?; + } + if self.obu.loop_filter_params.loop_filter_mode_deltas != [0, 0] { + self.invalid_element_value("loop_filter_mode_deltas")?; + } + return Ok(()); + } + + self.f(6, self.obu.loop_filter_params.loop_filter_level[0])?; + self.f(6, self.obu.loop_filter_params.loop_filter_level[1])?; + if sequence.num_planes > 1 + && self.obu.loop_filter_params.loop_filter_level[0] != 0 + && self.obu.loop_filter_params.loop_filter_level[1] != 0 + { + self.f(6, self.obu.loop_filter_params.loop_filter_level[2])?; + self.f(6, self.obu.loop_filter_params.loop_filter_level[3])?; + } + + self.f(3, self.obu.loop_filter_params.loop_filter_sharpness)?; + self.f(1, self.obu.loop_filter_params.loop_filter_delta_enabled)?; + if self.obu.loop_filter_params.loop_filter_delta_enabled { + self.f(1, self.obu.loop_filter_params.loop_filter_delta_update)?; + if self.obu.loop_filter_params.loop_filter_delta_update { + for i in 0..TOTAL_REFS_PER_FRAME { + // NOTE: Currently we have no way of checking if the value changed between + // frames, always update the value to make sure the decoder will recreate + // the same state. + const UPDATE_REF_DELTA: bool = true; + if UPDATE_REF_DELTA { + self.su(1 + 6, self.obu.loop_filter_params.loop_filter_ref_deltas[i])?; + } + } + + for i in 0..2 { + // NOTE: Same as above + const UPDATE_MODE_DELTA: bool = true; + if UPDATE_MODE_DELTA { + self.su(1 + 6, self.obu.loop_filter_params.loop_filter_mode_deltas[i])?; + } + } + } + } + + Ok(()) + } + + /// Writes AV1 5.9.12. Quantization params syntax + fn quantization_params(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + self.f(8, self.obu.quantization_params.base_q_idx)?; + self.read_delta_q(self.obu.quantization_params.delta_q_y_dc)?; + if sequence.num_planes > 1 { + if sequence.color_config.separate_uv_delta_q { + self.f(1, self.obu.quantization_params.diff_uv_delta)?; + } else if self.obu.quantization_params.diff_uv_delta { + self.invalid_element_value("diff_uv_delta")?; + }; + + self.read_delta_q(self.obu.quantization_params.delta_q_u_dc)?; + self.read_delta_q(self.obu.quantization_params.delta_q_u_ac)?; + + if self.obu.quantization_params.diff_uv_delta { + self.read_delta_q(self.obu.quantization_params.delta_q_v_dc)?; + self.read_delta_q(self.obu.quantization_params.delta_q_v_ac)?; + } else { + if self.obu.quantization_params.delta_q_v_dc != 0 { + self.invalid_element_value("delta_q_v_dc")?; + } + if self.obu.quantization_params.delta_q_v_ac != 0 { + self.invalid_element_value("delta_q_v_ac")?; + } + } + } else { + if self.obu.quantization_params.delta_q_u_dc != 0 { + self.invalid_element_value("delta_q_u_dc")?; + } + if self.obu.quantization_params.delta_q_u_ac != 0 { + self.invalid_element_value("delta_q_u_ac")?; + } + if self.obu.quantization_params.delta_q_v_dc != 0 { + self.invalid_element_value("delta_q_v_dc")?; + } + if self.obu.quantization_params.delta_q_v_ac != 0 { + self.invalid_element_value("delta_q_v_ac")?; + } + } + + self.f(1, self.obu.quantization_params.using_qmatrix)?; + if self.obu.quantization_params.using_qmatrix { + self.f(4, self.obu.quantization_params.qm_y)?; + self.f(4, self.obu.quantization_params.qm_u)?; + + if !sequence.color_config.separate_uv_delta_q { + if self.obu.quantization_params.qm_v != self.obu.quantization_params.qm_u { + self.invalid_element_value("qm_v")?; + } + } else { + self.f(4, self.obu.quantization_params.qm_v)?; + } + } + + Ok(()) + } + + /// Writes AV1 5.9.13. Delta quantizer syntax + fn read_delta_q(&mut self, delta_q: i32) -> SynthesizerResult<()> { + self.f(1, delta_q != 0)?; + if delta_q != 0 { + self.su(1 + 6, delta_q)?; + } + Ok(()) + } + + /// Writes AV1 5.9.14. Segmentation params syntax + fn segmentation_params(&mut self) -> SynthesizerResult<()> { + self.f(1, self.obu.segmentation_params.segmentation_enabled)?; + if self.obu.segmentation_params.segmentation_enabled { + if self.obu.primary_ref_frame == PRIMARY_REF_NONE { + if !self.obu.segmentation_params.segmentation_update_map { + self.invalid_element_value("segmentation_update_map")?; + } + + if self.obu.segmentation_params.segmentation_temporal_update { + self.invalid_element_value("segmentation_temporal_update")?; + } + + if !self.obu.segmentation_params.segmentation_update_data { + self.invalid_element_value("segmentation_update_data")?; + } + } else { + self.f(1, self.obu.segmentation_params.segmentation_update_map)?; + if self.obu.segmentation_params.segmentation_update_map { + self.f(1, self.obu.segmentation_params.segmentation_temporal_update)?; + } + self.f(1, self.obu.segmentation_params.segmentation_temporal_update)?; + } + + if self.obu.segmentation_params.segmentation_update_data { + for i in 0..MAX_SEGMENTS { + for j in 0..SEG_LVL_MAX { + let feature_enabled = self.obu.segmentation_params.feature_enabled[i][j]; + self.f(1, feature_enabled)?; + + if feature_enabled { + let bits_to_read = FEATURE_BITS[j] as usize; + let limit = FEATURE_MAX[j]; + let signed = FEATURE_SIGNED[j]; + + let value = i32::from(self.obu.segmentation_params.feature_data[i][j]); + if signed { + let clipped_value = clip3(-limit, limit, value); + self.su(bits_to_read + 1, clipped_value)?; + } else { + let clipped_value = clip3(0, limit, value); + self.f(bits_to_read, u32::try_from(clipped_value)?)?; + } + } + } + } + } else { + if self.obu.segmentation_params.feature_enabled + != [[false; SEG_LVL_MAX]; MAX_SEGMENTS] + { + self.invalid_element_value("feature_enabled")?; + } + if self.obu.segmentation_params.feature_data != [[0; SEG_LVL_MAX]; MAX_SEGMENTS] { + self.invalid_element_value("feature_data")?; + } + } + } + + Ok(()) + } + + /// Writes AV1 5.9.15. Tile info syntax + fn tile_info(&mut self) -> SynthesizerResult<()> { + // From AV1 5.9.9. Compute image size function + self.f(1, self.obu.tile_info.uniform_tile_spacing_flag)?; + if self.obu.tile_info.uniform_tile_spacing_flag { + if self.obu.tile_info.tile_cols != 1 + && self.obu.tile_info.tile_cols_log2 != 0 + && self.obu.tile_info.tile_rows != 1 + && self.obu.tile_info.tile_rows_log2 != 0 + { + // TODO: Allow more then single tile + log::error!("Only 1x1 tiles frame is currently supported"); + return Err(SynthesizerError::Unsupported); + } + + const INCREMENT_TILE_COLS_LOG2: u32 = 0; + const INCREMENT_TILE_ROWS_LOG2: u32 = 0; + + self.f(1, INCREMENT_TILE_COLS_LOG2)?; + self.f(1, INCREMENT_TILE_ROWS_LOG2)?; + } else { + // TODO + log::error!("Only uniformly sized tiles are currently supported"); + return Err(SynthesizerError::Unsupported); + } + + Ok(()) + } + + /// Writes AV1 5.9.17. Quantizer index delta parameters syntax + fn delta_q_params(&mut self) -> SynthesizerResult<()> { + if self.obu.quantization_params.base_q_idx > 0 { + self.f(1, self.obu.quantization_params.delta_q_present)?; + + if self.obu.quantization_params.delta_q_present { + self.f(2, self.obu.quantization_params.delta_q_res)?; + } + } else if self.obu.quantization_params.delta_q_present { + self.invalid_element_value("delta_q_present")?; + } + + Ok(()) + } + + /// Writes AV1 5.9.18. Loop filter delta parameters syntax + fn delta_lf_params(&mut self) -> SynthesizerResult<()> { + if self.obu.quantization_params.delta_q_present { + if self.obu.allow_intrabc { + self.f(1, self.obu.loop_filter_params.delta_lf_present)?; + + if self.obu.loop_filter_params.delta_lf_present { + self.f(2, self.obu.loop_filter_params.delta_lf_res)?; + self.f(1, self.obu.loop_filter_params.delta_lf_multi)?; + } + } else if self.obu.loop_filter_params.delta_lf_present { + self.invalid_element_value("delta_lf_present")?; + } + } + + Ok(()) + } + + /// Writes AV1 5.9.19. CDEF params syntax + fn cdef_params(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + if self.obu.coded_lossless || self.obu.allow_intrabc || !sequence.enable_cdef { + if self.obu.cdef_params.cdef_bits != 0 { + self.invalid_element_value("cdef_bits")?; + } + + if self.obu.cdef_params.cdef_y_pri_strength[0] != 0 { + self.invalid_element_value("cdef_y_pri_strength")?; + } + + if self.obu.cdef_params.cdef_y_sec_strength[0] != 0 { + self.invalid_element_value("cdef_y_sec_strength")?; + } + + if self.obu.cdef_params.cdef_uv_pri_strength[0] != 0 { + self.invalid_element_value("cdef_uv_pri_strength")?; + } + + if self.obu.cdef_params.cdef_uv_sec_strength[0] != 0 { + self.invalid_element_value("cdef_uv_sec_strength")?; + } + + if self.obu.cdef_params.cdef_damping != 3 { + self.invalid_element_value("cdef_damping")?; + } + + return Ok(()); + } + + self.f(2, self.obu.cdef_params.cdef_damping - 3)?; + self.f(2, self.obu.cdef_params.cdef_bits)?; + + for i in 0..(1 << self.obu.cdef_params.cdef_bits) { + self.f(4, self.obu.cdef_params.cdef_y_pri_strength[i])?; + + let mut cdef_y_sec_strength = self.obu.cdef_params.cdef_y_sec_strength[i]; + if cdef_y_sec_strength == 4 { + cdef_y_sec_strength -= 1; + } + + self.f(2, cdef_y_sec_strength)?; + + if sequence.num_planes > 1 { + self.f(4, self.obu.cdef_params.cdef_uv_pri_strength[i])?; + + let mut cdef_uv_sec_strength = self.obu.cdef_params.cdef_uv_sec_strength[i]; + if cdef_uv_sec_strength == 4 { + cdef_uv_sec_strength -= 1; + } + + self.f(2, cdef_uv_sec_strength)?; + } + } + + Ok(()) + } + + /// Writes AV1 5.9.20. Loop restoration params syntax + fn lr_params(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + if self.obu.all_lossless || self.obu.allow_intrabc || !sequence.enable_restoration { + if self.obu.loop_restoration_params.frame_restoration_type + != [FrameRestorationType::None; MAX_NUM_PLANES] + { + self.invalid_element_value("frame_restoration_type")?; + } + + if self.obu.loop_restoration_params.uses_lr { + self.invalid_element_value("uses_lr")?; + } + + return Ok(()); + } + + let mut uses_lr = false; + let mut uses_chroma_lr = false; + for i in 0..MAX_NUM_PLANES { + let lr_type = self.obu.loop_restoration_params.frame_restoration_type[i]; + self.f(2, lr_type as u32)?; + + if lr_type != FrameRestorationType::None { + uses_lr = true; + + if i > 0 { + uses_chroma_lr = true; + } + } + } + + if uses_lr ^ self.obu.loop_restoration_params.uses_lr { + self.invalid_element_value("uses_lr")?; + } + if uses_chroma_lr ^ self.obu.loop_restoration_params.uses_chroma_lr { + self.invalid_element_value("uses_chroma_lr")?; + } + + if uses_lr { + if sequence.use_128x128_superblock { + if self.obu.loop_restoration_params.lr_unit_shift == 0 { + self.invalid_element_value("lr_unit_shift")?; + } + + self.f(1, self.obu.loop_restoration_params.lr_unit_shift - 1)?; + } else { + self.f(1, self.obu.loop_restoration_params.lr_unit_shift)?; + if self.obu.loop_restoration_params.lr_unit_shift != 0 { + self.f(1, self.obu.loop_restoration_params.lr_unit_shift > 1)?; + } + } + + if sequence.color_config.subsampling_x + && sequence.color_config.subsampling_y + && uses_chroma_lr + { + self.f(1, self.obu.loop_restoration_params.lr_uv_shift)?; + } else if self.obu.loop_restoration_params.lr_uv_shift != 0 { + self.invalid_element_value("lr_uv_shift")?; + } + } + + Ok(()) + } + + /// Writes AV1 5.9.21. TX mode syntax + fn read_tx_mode(&mut self) -> SynthesizerResult<()> { + if self.obu.coded_lossless { + if self.obu.tx_mode != TxMode::Only4x4 { + self.invalid_element_value("TxMode")?; + } + } else { + self.f(1, self.obu.tx_mode_select)?; + + if (self.obu.tx_mode_select != 0 && self.obu.tx_mode != TxMode::Select) + || (self.obu.tx_mode_select == 0 && self.obu.tx_mode != TxMode::Largest) + { + self.invalid_element_value("TxMode")?; + } + } + + Ok(()) + } + + /// Writes AV1 5.9.22. Skip mode params syntax + fn skip_mode_params(&mut self) -> SynthesizerResult<()> { + // TODO: Implement if needed + Ok(()) + } + + /// Writes AV1 5.9.23. Frame reference mode syntax + fn frame_reference_mode(&mut self) -> SynthesizerResult<()> { + if self.obu.frame_is_intra { + if self.obu.reference_select { + self.invalid_element_value("reference_select")?; + } + } else { + self.f(1, self.obu.reference_select)?; + } + + Ok(()) + } + + /// Writes AV1 5.9.23. Frame reference mode syntax + fn global_motion_params(&mut self) -> SynthesizerResult<()> { + if self.obu.frame_is_intra { + return Ok(()); + } + + for ref_ in ReferenceFrameType::Last as usize..=ReferenceFrameType::AltRef as usize { + let is_global = self.obu.global_motion_params.is_global[ref_]; + let is_rot_zoom = self.obu.global_motion_params.is_rot_zoom[ref_]; + let is_translation = self.obu.global_motion_params.is_translation[ref_]; + let gm_type = self.obu.global_motion_params.gm_type[ref_]; + + let expected_type = match (is_global, is_rot_zoom, is_translation) { + (false, _, _) => WarpModelType::Identity, + (true, true, _) => WarpModelType::RotZoom, + (true, false, true) => WarpModelType::Translation, + (true, false, false) => WarpModelType::Affine, + }; + + if expected_type != gm_type { + self.invalid_element_value("GmType")?; + } + + self.f(1, is_global)?; + if is_global { + self.f(1, is_rot_zoom)?; + if is_rot_zoom { + } else { + self.f(1, is_translation)?; + } + } + + if gm_type >= WarpModelType::RotZoom { + self.read_global_param(gm_type, ref_, 2)?; + self.read_global_param(gm_type, ref_, 3)?; + if gm_type == WarpModelType::Affine { + self.read_global_param(gm_type, ref_, 4)?; + self.read_global_param(gm_type, ref_, 5)?; + } + } + + if gm_type >= WarpModelType::Translation { + self.read_global_param(gm_type, ref_, 0)?; + self.read_global_param(gm_type, ref_, 1)?; + } + } + + Ok(()) + } + + /// Writes AV1 5.9.25. Global param syntax + fn read_global_param( + &mut self, + _gm_type: WarpModelType, + _ref_: usize, + _idx: u32, + ) -> SynthesizerResult<()> { + // TODO + log::warn!( + "Syntax element read_global_param() is not currently supported. Use GmType=IDENTITY" + ); + Err(SynthesizerError::Unsupported) + } + + /// Writes AV1 5.9.30. Film grain params syntax + fn film_grain_params(&mut self, sequence: &'o SequenceHeaderObu) -> SynthesizerResult<()> { + if !sequence.film_grain_params_present || (!self.obu.show_frame && !self.obu.showable_frame) + { + return Ok(()); + } + + self.f(1, self.obu.film_grain_params.apply_grain)?; + if !self.obu.film_grain_params.apply_grain { + return Ok(()); + } + + Err(SynthesizerError::Unsupported) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::codec::av1::parser::CdefParams; + use crate::codec::av1::parser::ChromaSamplePosition; + use crate::codec::av1::parser::ColorConfig; + use crate::codec::av1::parser::TileInfo; + use crate::codec::av1::parser::MAX_TILE_COLS; + use crate::codec::av1::parser::MAX_TILE_ROWS; + + #[test] + fn sequence_header_obu_test25fps() { + // Extraced from ./src/codec/av1/test_data/test-25fps.ivf.av1 + const SEQ_HDR_RAW: [u8; 13] = + [0x0a, 0x0b, 0x00, 0x00, 0x00, 0x04, 0x3c, 0xff, 0xbd, 0xff, 0xf9, 0x80, 0x40]; + + let seq_hdr = SequenceHeaderObu { + obu_header: ObuHeader { + obu_type: ObuType::SequenceHeader, + extension_flag: false, + has_size_field: true, + temporal_id: 0, + spatial_id: 0, + }, + + seq_profile: Profile::Profile0, + num_planes: 3, + still_picture: false, + reduced_still_picture_header: false, + timing_info_present_flag: false, + initial_display_delay_present_flag: false, + operating_points_cnt_minus_1: 0, + frame_width_bits_minus_1: 8, + frame_height_bits_minus_1: 7, + max_frame_width_minus_1: 319, + max_frame_height_minus_1: 239, + frame_id_numbers_present_flag: false, + use_128x128_superblock: true, + enable_filter_intra: true, + enable_intra_edge_filter: true, + enable_interintra_compound: true, + enable_masked_compound: true, + enable_warped_motion: true, + enable_dual_filter: true, + enable_order_hint: true, + enable_jnt_comp: true, + enable_ref_frame_mvs: true, + seq_choose_screen_content_tools: true, + seq_force_screen_content_tools: SELECT_SCREEN_CONTENT_TOOLS as u32, + seq_choose_integer_mv: true, + seq_force_integer_mv: SELECT_INTEGER_MV as u32, + order_hint_bits_minus_1: 6, + order_hint_bits: 7, + enable_superres: false, + enable_cdef: true, + enable_restoration: true, + color_config: ColorConfig { + high_bitdepth: false, + mono_chrome: false, + color_description_present_flag: false, + color_range: false, + subsampling_x: true, + subsampling_y: true, + chroma_sample_position: ChromaSamplePosition::Unknown, + separate_uv_delta_q: false, + ..Default::default() + }, + film_grain_params_present: false, + + ..Default::default() + }; + + let mut buf = Vec::::new(); + Synthesizer::<'_, SequenceHeaderObu, _>::synthesize(&seq_hdr, &mut buf).unwrap(); + assert_eq!(buf, SEQ_HDR_RAW); + } + + #[test] + fn sequence_header_obu_av1_annexb() { + // Extraced from: ./src/codec/av1/test_data/av1-annexb.ivf.av1 + const SEQ_HDR_RAW: [u8; 12] = + [0x0a, 0x0a, 0x00, 0x00, 0x00, 0x02, 0xaf, 0xff, 0xbf, 0xff, 0x30, 0x08]; + + let seq_hdr = SequenceHeaderObu { + obu_header: ObuHeader { + obu_type: ObuType::SequenceHeader, + extension_flag: false, + has_size_field: true, + temporal_id: 0, + spatial_id: 0, + }, + + seq_profile: Profile::Profile0, + num_planes: 3, + still_picture: false, + reduced_still_picture_header: false, + timing_info_present_flag: false, + initial_display_delay_present_flag: false, + operating_points_cnt_minus_1: 0, + frame_width_bits_minus_1: 5, + frame_height_bits_minus_1: 5, + max_frame_width_minus_1: 63, + max_frame_height_minus_1: 63, + frame_id_numbers_present_flag: false, + use_128x128_superblock: true, + enable_filter_intra: true, + enable_intra_edge_filter: true, + enable_interintra_compound: true, + enable_masked_compound: true, + enable_warped_motion: true, + enable_dual_filter: true, + enable_order_hint: true, + enable_jnt_comp: true, + enable_ref_frame_mvs: true, + seq_choose_screen_content_tools: true, + seq_force_screen_content_tools: SELECT_SCREEN_CONTENT_TOOLS as u32, + seq_choose_integer_mv: true, + seq_force_integer_mv: SELECT_INTEGER_MV as u32, + order_hint_bits_minus_1: 6, + order_hint_bits: 7, + enable_superres: false, + enable_cdef: true, + enable_restoration: true, + color_config: ColorConfig { + high_bitdepth: false, + mono_chrome: false, + color_description_present_flag: false, + color_range: false, + subsampling_x: true, + subsampling_y: true, + chroma_sample_position: ChromaSamplePosition::Unknown, + separate_uv_delta_q: false, + ..Default::default() + }, + film_grain_params_present: false, + + ..Default::default() + }; + + let mut buf = Vec::::new(); + Synthesizer::<'_, SequenceHeaderObu, _>::synthesize(&seq_hdr, &mut buf).unwrap(); + assert_eq!(buf, SEQ_HDR_RAW); + } + + #[test] + fn temporal_delim_obu() { + const TD_RAW: [u8; 2] = [0x12, 0x00]; + + let td = TemporalDelimiterObu { + obu_header: ObuHeader { + obu_type: ObuType::TemporalDelimiter, + extension_flag: false, + has_size_field: true, + temporal_id: 0, + spatial_id: 0, + }, + }; + + let mut buf = Vec::::new(); + Synthesizer::<'_, TemporalDelimiterObu, _>::synthesize(&td, &mut buf).unwrap(); + + assert_eq!(buf, TD_RAW); + } + + #[test] + fn frame_header_obu() { + let _ = env_logger::try_init(); + + const WIDTH: u32 = 512; + const HEIGHT: u32 = 512; + + let seq = SequenceHeaderObu { + obu_header: ObuHeader { + obu_type: ObuType::SequenceHeader, + extension_flag: false, + has_size_field: true, + temporal_id: 0, + spatial_id: 0, + }, + + seq_profile: Profile::Profile0, + + frame_width_bits_minus_1: 16 - 1, + frame_height_bits_minus_1: 16 - 1, + max_frame_width_minus_1: (WIDTH - 1) as u16, + max_frame_height_minus_1: (HEIGHT - 1) as u16, + + seq_force_integer_mv: SELECT_INTEGER_MV as u32, + + enable_order_hint: true, + order_hint_bits: 8, + order_hint_bits_minus_1: 7, + num_planes: 3, + + color_config: ColorConfig { + subsampling_x: true, + subsampling_y: true, + ..Default::default() + }, + + ..Default::default() + }; + + let frame = FrameHeaderObu { + obu_header: ObuHeader { + obu_type: ObuType::FrameHeader, + extension_flag: false, + has_size_field: true, + temporal_id: 0, + spatial_id: 0, + }, + + frame_type: FrameType::KeyFrame, + frame_is_intra: true, + primary_ref_frame: PRIMARY_REF_NONE, + refresh_frame_flags: 0xff, + error_resilient_mode: true, + + reduced_tx_set: true, + tx_mode_select: 1, + tx_mode: TxMode::Select, + + tile_info: TileInfo { + uniform_tile_spacing_flag: true, + tile_cols: 1, + tile_rows: 1, + tile_cols_log2: 0, + tile_rows_log2: 0, + width_in_sbs_minus_1: { + let mut value = [0u32; MAX_TILE_COLS]; + value[0] = WIDTH / 64 - 1; + value + }, + height_in_sbs_minus_1: { + let mut value = [0u32; MAX_TILE_ROWS]; + value[0] = HEIGHT / 64 - 1; + value + }, + ..Default::default() + }, + + cdef_params: CdefParams { cdef_damping: 3, ..Default::default() }, + + superres_denom: SUPERRES_NUM as u32, + upscaled_width: WIDTH, + frame_width: WIDTH, + frame_height: HEIGHT, + render_width: WIDTH, + render_height: HEIGHT, + + ..Default::default() + }; + + let mut buf = Vec::::new(); + Synthesizer::<'_, SequenceHeaderObu, _>::synthesize(&seq, &mut buf).unwrap(); + + Synthesizer::<'_, FrameHeaderObu, _>::synthesize(&frame, &seq, &mut buf).unwrap(); + + // TODO actual test + + let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); + if write_to_file { + let mut out = std::fs::File::create("frame_header_obu.av1").unwrap(); + out.write_all(&buf).unwrap(); + out.flush().unwrap(); + } + } +} diff --git a/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1 b/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1 new file mode 100644 index 0000000000000000000000000000000000000000..1bdc6b4c675d03aabb4197c14f9d64f534831315 GIT binary patch literal 579 zcmV-J0=)f1OG!om03ZNCRxmL@06+j90000100001000000000N0ssI2000000001$ z1d{{-5DN$Z00087|G)n*2!jML6aXOr004jjz<}FvNjdpAZ%GAtK|?t;|5+}LhN>Hm z*&=8vwruHfh~s9$j4wMYBt0g2X0nWoL8+k(zs+_noGMFo?IFe!V2 zx&XC|Djk9)sX>jn0ImpGQ#2(g6iiy4k?M2c?}jMxj={0wrg4{3Wp!t|$oRkYyg7S@wS9Jc#lw4ZnanZZ6|HYk^ zar+yxEQO!^(rE%5T*0(0m6lnuAwVZ32g$Y{u07fbi===w^oI*)2Ke{WZrZg)fsau6 zMK%pX(Z?xFmIhC$?$>Evgdd0IoH+fSv8O#VPOr6RJGyIkf~R+PT?k(^0gwIpIb3mH RVcwIqMmH}rK|g?430Mco01yBG literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1.crc b/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1.crc new file mode 100644 index 00000000..e69de29b diff --git a/vendor/cros-codecs/src/codec/av1/test_data/gen_crcs.sh b/vendor/cros-codecs/src/codec/av1/test_data/gen_crcs.sh new file mode 100755 index 00000000..007b63e6 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/test_data/gen_crcs.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Generates the CRCs for all .av1 files in the current directory using ffmpeg. + +for f in `ls *.av1`; do + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5 +done diff --git a/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf new file mode 100644 index 0000000000000000000000000000000000000000..83b53b2710bcac516081e7bbfe0f2e7a755475ed GIT binary patch literal 223686 zcmV)GK)%03OG!om03ZNCRxmL@0q_7B000010002`000000001YDF6Tf000000000I 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

1SA13Uv$S0}t4Y(!=*&KXY}Ht?<|(h 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%0D0jgEYIqk87i^VBT**g}3+hw$2*E(f zK>9;fjjy8+wHLcb>pY2rViU%s<9%q)i$qCKk21EcNEKT>BpU3SH?_w_=6j+@m7mj>w>$~JF%L>Apv8?4aOX(LM4do04_LAQLB zlmv|A*b|%x@6JiQaHZ3ga+k64$%&1=4tx}TXA$zzU%p;rk94Xy(S0=`Be{+^{>XIj zpG?<4^oIX5E(r#BfzSXzdH~>o0nq+oSDcCAH!}Fe;-idIpwR6%^EBDS9CRg+p`VKs zZ?b%NsJ_SMXQ<431!u z>=|CCD7AT@_%r%z_lY;hvje(h)t-(fFMwGg!gC1rh0GpNMn+CMqFsWuNBD zU~9-Wxp3WnIW54>_!nGo5}vM}=N38c-)ku~p(#mwy(-JBrNOpBaRUR80!4_PB(59P zms%Kmk5SFfLCW?6Wp0rYhlDz7vbInGptrOH!R4nyRV6#}Cvra4C@yx2Ra!f~;>>|d zK{&9B7~Pv6`yc*i|8rn^qkqFOtnZK25q<&k0{xGf`4f@ZnlUch5qH_elCMr?%j}mR zH0MC<2OF*J`Mv-d%se(uf|68K`r~C|<39T9ltzehfNe(RmKZx;PK75!z*4BWE0TI{>4$ z&5s`2(DeSJP_OQ=CiO@cm~9Bgs5ZaY11J}8IbLW=84nk2DP+8(ifiFC7(m|TFsX&} z#JyITXl!$x$1PrJhj$6mzT0n=@ zibgbNz-`tOOdUiNYv@ttTgj*%yI&Vwv@Am^HuLo&qHTQUz+yu zdpD~I& z3=aI@tE@#`eomkt|MyW9E+rR4_)&cmBs@s#uYFrj>e<{hpNOJz+RqLFf zCAR65I}86@CW(HHr4;h^%p$1rSe{(qBGmL6aNG}^WPsrocMlTZ?H%EtW&! zpCkeq8Riy!vcATG#t^!#D*B7`$^i&2t}JLMan#91T*1&;uT-n?hxE_c?pt;B1j1Q9 zX~-xSGD9(2*+xQrwv)e%tN`C9+fff@d0&mz1v8hOG1wRwswEQf9}^R~Rva_0f}LAK z>83A0WwEWBUD4f^=t_LSe#FC;^`h9Gd-_B^*k-vVNpVe{d$TbE9hG4Q^Ucdj6tgA@_5dZHv8sY*9rFDSGd0%;qT2I!@3xz-F@$@ z!p*u-s3I>>~4xa7m7=}#Aq#dFfkE|SHqc)o7h_Tf^dw$8|f zMm#e1hDB$gu2SzUc+qlrdl7BztEAv&M=yK$X~-9drav8WXbN7^TAINjw+<|T-JH0M zcYuI8#u|Nbu*Lj8H(DI3YanR-JpuKaXkf9H6(I2n9ZPY}boVzDqoSGsv_I#gZjaEecF zTPwQ6dmjj!2R$V-wj>`L&XsW^N&99NNNzZ*>0mD?To$O90ec>3Dgyu1r|IUt5FzSOhmp;Kel?~hreX7-Usw7W~Wk6X*XaXlU7=4b@ zKW&c^SCCq(f^w%`$-E=Qb*+EFRnp(y3A#0=-vr2TooO!}yCL*Hl(V#WpR#&dPuBg} z9b_JMjSA4invYXuqMtmm7iV%)>HM>6_1RdFK7q91?BAzU%Dy0oXf?CcIx#eEuVIga z(W`Z-)QaLOFy=kA_(-ACr7aBW1L2-Mx#JF5lk02nOgZitX`RVOYkJD5qsFgwSnAPh zp)VCL1fIv;(H+3uuz0{iTDC1Q7#X;O)5?E^qW2Di=Py6gK^W-v$v z9Yw|egZsmc8=w3zkI@)Q&Bof4v>cuhkUAVV^kMDxzFupjUK52a zd=z|rar+_;Zi7BB&L=J>oMyiQQTbWp79=5R8r;@9>sPzQRJ-5&e2-g0C8G|EEzaoZ z@9H(D)pfb4PTdiz&z-QH45&*~qA*Vrm*j(t;pW3~=%;K%~!GTtm0$hJSx_eq;0EWmH-j~a-3C?DmHdHR-yr@X}is6mS zm4Me!tWa|A6Yfd`+LDf}R1Hz4b%OJ)V+1>)t^Qrbz+h-Iz-yZxX3KqOsFI~REea;+ zVph@85=DvsJUO1KdqvdFWItNC7yY=?NcHdLa{wg0~yDGn5GMOcO^t*;N z+-!;(CNBzK^df~jJy->Xf(zpmDDjF(7)rE)bGMM^{)jjoh--XjC6BLW8rcGJhgR*Nac z$%pz>&1DcOL+q-w?$BX2SzL@hryPCE#Nvo0yrvYmDRY<&Q!}zlNM=4cSFS@YM^<(! zRpm8CzFO^}%8X}nW$sJ6%fg1+Mpf-_%axiFqvW-3$oJ`kSz^Onu{5Qm0PddgolsSS zky_+M!*Iyt8mRz=;5Z>8sV&ZiTv#Y;BctKe)&9yjXH;k4VxVXD@{A5JJVK~+9$7UjHqh$fv?j{FM`n3a~knrg{Y9 z`MDlHC)+1qZ0VOK5!3ApU@RyW3~ZJ+M#0NB9T@jcQ zHCS{!tfMBBLqM|U7Q$I@7Z!Fi9)1`E+jx7IaJtWoy@)c`Vr(VEh&_s7W;P+>{?CrF;{*3Oou>F%0B=k=TGJm4|SK%C|QH`ZR`w1&&s);C81+N72f4Gr>FP`(W z0Riuj(9sc_^3OYloIp+^l>8N_LjC43XCSabP1xMC-sg@q{wN9f+t_Nxg_06)4I!!! zhB2&(<-9hOPdoyc{u=l!4hFdelzo5%>r8*&N{?>9T*)}>th zC5x|$xIQSXO(Z$3jew1;_lb}EyZRy>85T5ziM|1i{Fm_JT;-R8v}+Z+VnkVQonZ?v zXm85PGJC9^)`GjLzFZQi5!4>P6B$i?V`%0_!gM`Ou$imi&Y?Mb-5M!kx5|L& z4GMK$W|eVax3soJK;F5q`HBa~q$M#5DziKDh8vPY8W0=C*O}62c>~jm=CJQg{B5=e{h+3 zQmA$F>RtTG5O>rJ4y=l7cqx1l4{8s~sQ8WDSYhxPkYNEZy&(l|MA|gUu;grv8H=ZEM+sVQ@xG5@(6mb-4 zvl5U@(fV_{t;`O4TE90X(U(7?bpXjOXgVu=fa^&T69g07&~y?!fjk=n0*;m1mghUa zZUEB;)WR74_Ttk45_1axbgMB!T;$PJrG@8tJ8n*(JN&Fb9}+s3>r`yzn>GrY1J69x|{%3$7P8$10x^W@h@YiCIG6l&@S=%b16s%ovzd1IP#JTNJ8u17l%|?;npNWeYJvp^L^?7S_Jn z&p;i@II{DncWkkF;z#)K>-_!h*-1mgVXN*au^%GkfT=3cRnztFLkeXJ86zZTxu)Qy zW~7`@ymuw;J9m!PE@+4c@6R|K`@AwlM1bDzBbp0m>lqUXv<1mm$0Z^IFD$u*n$*YS zjZU3yC19ofdMPBTkTG#>3bLn5UT2pw=8ADWR8?wNi0H; zUKDWIJ<37W-IZzD$R8BygTeP_rKEW%;LkDL;~;QUbl)?-qnQBFJ{97S$QiJTNVIVP zSP6~bGth16uFdXd!J!d%4&R&j#ffqZ=)_1b%7r&~PPNkHCt>k-YNeCxD3f~*gE*o0 z$fW)x+Prk{c8;AGrBX-$XrC&L0>A5T-ou!;+hYPSc3)B~b!#NQZGBre)5swAd~X_0 zkSzP|gwHY{75$wERn3%Y&w-Y3PW7J#9ky*}{Q~v}Q4kEiFzNbD(!?N<3G2#~dsZ=1 z=6Gy>hucK>RMukyu9z>$=uzrx&k?Ro-f~pIJ-4zgGB-m}IVaU5(5&i;4B|W@>Ad69 zNaFNvWQQ{^4(MYMrmrFng5=9;Sc~FDGEDAy9B-9e9{hZmGh}U4P6t((gtw?Gm=Vm%7eDSfPZBk}c+_z939K-)aPq)y}7t&3hL1dSd%94>_TI z=Y?G*-;-J#XT%&1P*SXd`<>nDCSHcKC##Xdhr z-w;%U%puvb8KsrUH~R}-fh<9w%}A*z*@8;2Z=hLE3~zKsjK3$Bm+`$&#asWb zGk23;RDR=>y9`ev*dByD%O}g70So=cstifJW{oP+P@T45By1Mf*^7520p_3+y4|>? z59t(8${bX{7(gzKnTbCJw2ypwC*1Kq|9ydbp@iEbGIiNi9I~!!vL8@)k(*SRfLm?M zzeDapxl_||dr1v1vU>oqRQy5*4`2C~TQ-DPK0F*a>MTkJKy@Wp75hX|ocTFm7QTkc zPpBO<&AjO}ur6lR1pS5C_A;hIE~+k1H{x|4kSc?b9G*8SonJf~QL4tnR>tE8HoJ&$ z-}gi!G`fOc53fV4i2c$hdAD2a6dNQ)iR`2ky^Jl=NY+YupHIDORMvxmh^>>4Xt{$x$$jmWwfB}P~*!b zrXo(^5}@Oeh=2p1IDD2jp@f?N0RWu-XZ<}8db59VJW&6OzTmVLrPfd z6WJ>($*ruU3AAm1-#f_8h$9*y*qa$Evn~dQOlm);=LaVAw_>sqaimihGn0r*NO3Q+aN|DT zuk_q01DmxOfc5g1*D86I(f+PQE6ZnVwrWSd7O9EJ=tS%ZXhLvB+yc857MbsK8yufW zo;ZmYx{#3U2Wm|SmpdJ1rC^BaE+<-0rRkvN_|*_TWuoX*LMRsPlv9cih@!1d8c?6t zg{Lx9T7%&y?}~L4ugtI&KT`zlk{qx1 zy#<3Bu0LIx3;^bMZ3{pFz(Y%U;qTS`HtrNEL`;t;`j=X^-bR##^iyp{P9wH+Kl{;^ zT)8-*wou1iEMZq=dCeinChTP#$Wv4uv8&JP-uno$H>mxb%Kr$R7y3J%2=??nCo2 z$$6=jgL)QIDWo{hs4C>Sg|&xLftJDm0}o?)xmBUOi9TxF-A3~qv3PlHF;CT60di1i zm=ms=+oGN=11h}8p9g>TpUZ^;)0_Vj!t?ule_#mk002KQgflTj81V=27ls4XjH|Z4 z&v%=ai91+_qLsT*^D#aQHnzVzF~kC-L>ENY#u-Dpwapw_zU2)e53OSCCc_;FSO)eP zm!I&Y$J<43Bl65GL zDJ=eMc1dLl#B>H{pY%Mv$`uT)}7U&Q;E<{Ic}k${uFCTTM@cGlQiQz&w@$1n(A@wG!Z$xz<+0( zD1HPhSlx@d5oOBTR?m9(hg{Iq!KM~;Ukc4Hni@9L`$@WDKOO;Wb>**nS+;SnXEP6Q zXq+yCylEYs9t}H3`ZPn?iDEXX4i6Hc% z!5Ap7T}4-fu&yiLkoN=APyJCERXT;GVbFT6&ftT+cYmL|xc^2)H<8LUTxqVR?`hWT z1+>p7uKrbh_u3s#VgcAeVJ-nKO0I3g`a$z^h|aMyMs%K?Nrw6ECoLId(3-r7a6~M| z$S5d8L7TxVg`F0x`_dNb`Eim~{JJe#F{)Hn+xvGf*OtO||p$SR~e8+;HM-@JHxO8+3<04>9 z=)(s6^UmCblAW;jpv{y9N~2wkg}ejzK5pB>qAX5E7^M%keZlVCi{p4#{^gjQnyJI5 zp_W;Y!A7HeYe)~OwjX(3qGv0g>UR~@s8iZ8RI5Qa@rmx#pp*-Ny!K&kQlMv;s^q?R z%%T9+1tE{W^s!{<>8g9W=1;7Qrsx#RmMEv z$PE+MG(YZGniZSdFim$xF$X^3sX-C@18Kq}nd!W9d#b$-OdY=ww%_ov?1uYrGh*^= zjB=5Px5m6Rd0BCg=~!}{RSAPHJrYn+<9E4)N9j6c6|V@;ThPM)0@|1@ z;0h>aT7Trd{7>EnV0x?ng;?F6AO3$%_4AJPiaR)JU);+^*16`F@j-&>6D)u-v*_vF z3@f+;A<7D!+VhX*k@=ksqwby4N%H#Nwq^JXC#E^7a{*=&TwFfM%Nn*G;H#UHfM8i5 zo1m9!4KgVzUA3C&yO!V*wELn-&e!Q_fdU6;Zhc_}oAJz8o)^vXNOwvRVz!w)B8}VT zpvJ-V;g>c;`F>U3}W?X-Ha>BCRLIv z9lix`SgUp^3BJueJWI_;Pe^^-^d93w@`6E#qorDzl4ar4T^Ce8V^}2{96jo4`&&7~ zp)vtlmDlX-vV#JKj(lob39cClUsdI+7vPXQ#IVd&Q_iigmKFd}9h=`BgavcsP&pc- zn~-{?4X@1QVSnC%=mlwBsPVfFj>E3m*YroLtN%pY2cozBCmeS}{*#*V0{pWS|ARg! zp^=n0{V4GUB-_!*LQ}bbkqD4Y*Bt+oLCo17Zam{wr`eY(r~3ZXx&4OW+!D9)@>bGPF^3l za1z`)wm$Eoj-6y^deRKJ0c-A%&(Tap6EfD4$Po~)=JJ%lIA2Wy{@VkH&pdEon z#rwFi9RV24cs8gJ8d#l1qNWFeZ@}YqV+mzmOp>ti`R2b6{RBtAhaiERH}HQ8P?ptx z{guEnywY}LnjeH`Mfx=%e7UOD<*)+wlMY}1XLCOgdYgaK;h*`fpL7`He}@GY|ICg% z2znnxg?mj(6U~J1o|Td?^$M-@UW=H{_7~eby9#Dnzo9~xe^hM zGe0$9lW;NbZC!*!t!19}!}ZAt^#qMh1GaPHPIeV^ir3(C4g{_PPS0Y#RuP$;jI|U# zG+?98$8uBdQc-z&Sip+;AWI7~+a}~&3wJF^fXm$sPo3VUu4v9P*=NzB5Jt|dUL}N2 z&ESEmZykzq+8oK>xi51A)=DLW)Zgt0feE-9)dpj-mI@obEx@Yrog4Ym3K;Yxx|M+? z(#F-gqV;BF&O8pcE>=Mno#$}85!TNzZcm+F1ZVF?)945wz^7PFXS zrDe!ol)BqsCw}%IR)y7yzz(ElnmK+nTFO9&I-T4}UMc|wH_ltnI^p(8mdLmK`pYBd zz!AKGsP=dEMCWl#pfOslNtw?_x!gA6wKVs`h343Jeq@86!7OZKB1X?f=klB_5Iq{& zh>HcYHm=6Fr#Cq;6Y16~b7a=zs_-1KFf#@qbJGE@k5OTkZAjxBUn+{S!dz3e1&HFJ zg%mx|`@H|afnlR%8egK9jF4RZ;|DzeaPyx>Ak*9ai-H{R1ppBJ7X|qRHMjqL z697VQ_wO_oL=S-Ne^Y;I5TkvaU+ixkkJ~sRQGb%Y*S+NQsGtfPc-Xnhu7vi*cHdiD zbR=*xWKmo?<7VW36D8TX?g>MM&0ITbyBa^ss%jO98e5OqR-mC{84z&SDsrBco1v2G zF!hVLg-K&tj_=&bKUige#)0y%V|&Z5%KNX`z%%5!4+`rFz<1Z5nvqDik|#L+>~e}N za?WKG=J#&T*j;uAp}R(?G~pzuChQnNtb5V-E|^+fT&?W#uFhudU86;~O7uuPh3~$K z{FMrSIt|bwgBHVS>ja}IFtW(ZrBTg~@JGNM*3>%(Hc@B#IH^wCE*rY#|AW4xFi7AxseqQ=`eUnpwZ&w&PxM|?j{jf$>5~4R;T_~Mv-Kj?0^UEjG z%HSOhx6brIe-c3%-)m9JFCQU5FJ1=aJ?#aVm2MvUC*j@Nkv4D0pFsWbiM#)NA`XP! z{$FMVgf9R*0DRz2JjnsL4d=Uw7(=xm4?irEO}t zn`(Q5L)J0vhlt#CZ}yErKKqHn47Flly-~orPNz zS`)@Wx}*dtKf2acq#Nmu1(uXpLQ+5^q?YdPR2rm9a#=bTDOp6MJC+t#8a}S~K97IH z{AS*nbKWy&^28sKD!{BF7m;l4fEEDW!- zJE5~n$Q$i9&|>7>>Ow)urCOfc>CFK&yoyiQ7W*eEnL`0}YdfW+!QVIxj>()N5yTUB z9ZM!dU$-hELUB3T=jNwOPrwb`jGyE5iSj4&fy4tN<_`pjj5D?w$Xm# zXvyYm{yks={DEr{LT-JjaBK4=)(4d_q7k|b?=#^F8EJOJJiz>J+Q~RgKYYqcJ2 z6H;h~37BOM^FOv06<;`Ub^ru6QJ-+1ToE~OE>?s-ult@f0jXpoSM}jib2E=!4#~7Y zRcz=^dPzrn=*i#)KLI161HkJw%P8q?-Ijd8Z0Z3Xkjsw&I2>WSCc@hEBytasf7`=h zy31GeGvcu)L{Mzc{~-8f0jAwq0k&x*k;l&ED3RaFk%9>y zuv)C)E&GNQVWYs(2?v)qHma5a?-4oui^wNzKDR&aw0m)f{8Kih|1oj?V-~iG^{p^R zX0IEDiCzYdxojvuC$zo zol-5#NC5PfFw>94ynIztZ0hy)69$}|mp{*N5Z*P*Dyo4qXR>cMwmDQ|EY1XPADnK7 zql_dk>{4DlO=k5`)EM=|1RfIgDJ5=Jjd&!3n8U_E6u5_s?&2x zoU2BJtg-PAmW2K6%sS*dXt$LprH{Siykr9`ZH<9C}+uOY1fDQHG9c_N8c>1-DAlyfoshR0izw;`Rk6Rc4j)+O} zrMmPBD>~kb%sy{=ip8@ZNcYC@S*Y0yyjD=~xBljYDxt+9j+}@h5xt@RDIqbBkDTSU z`7K4j*6Rx8_%8Q9Eim}T+({JT(NY?z8=40%d39GE&>fSX`jLz#aj>& zx5y5rPEbCKGFGu(sAE(l)q|PU#gAhduDpjO>nL;4&RZ z{nYPPG|O~!l`ekleO_?ai{42eqlLT|wZ8Xu*_LDefYi~uW+`D}gaOqXB@_WYQwh-5 zeR^KWh!Wz)tD|5Fm*MN3iph9AnOsI{G%MKD!xgcpah!fdIX64w-o9(Tx?pB!7HGTo zt({dFf7{3*{p(XLOV$R%Apd>NI&+0_hPxz}RUFQbK~n~rTOT%j?kEK0tz>In)V64c zz#Wa>GfQ#O>Fmn({t{3YOtK^30!}B+MqYIz(n zvl{YGR^La5Lq6lBiMFY@6KbWAK;4D~azF*HT&cCHOkCbHxbK8Z)i{V>E$YUzB4LKW z@v@Jn#x~3`51<1KWU>@`6#c}CY}5P;+{zk)ZE=0id}t8pjnSVwNjy?-b~g<{aNSvp z@iAxLjoY?PeeP!p;UtL`Eb941Xcnn`9&GXa4;eeRG}-uRqvl}*A<72WI4u$Ew=0GD zp^1*(C)Zvzoj&i~^6fUH7pk;j0Q2iLmR<6x!ZpFfj-nqMvs$%mVWiJFssuBNbxz73 zkM$;yE9>C6FxdA7>mk;1S&XATCs8B#m1cbAHFZZkV5B22(#syV2&n871{GgH&OALi zZ$YKn=(#eN7R={Uj$B<`PblRWqROPq5`%d7zvk9l+2W>|@=SdulvvMkyV=y| zIT|{4SJ)sa_G{Ei?k=8il_f|P?Nx%PP?Khg%k#sJ10br z)YoV7>a>o>uD+?=BUwi>Yh<55=#~R!_hVZ_)l(5AHSJr}Leo5wbOebue!!hWj(4I? zJas>{BJ*pM&PaB_eXP#q=&L*r=Yn?o^88xVLnnzp6+b-g@8P zHNK_{?24TWcUs*HBhmvy^lX?&?+Q!UzVHe$x-JNb9>2pjf957g$yQMvlmGVRTW;H) zD~Zy`r<>Et+KT7JjC{X5fiN|!`Q4g#@d+NRmnP6Q|Ma7J`+DNZRHc6nZRM zMWb;J3dYnv(?zq-@6_6@klif#)`{DAV`-706uJ9yqXycHS7Br9BWVA##!&9ew0MIA z*PY;Omr!brEfhWio=7w2?GM#96H?DOJmJP8q~@rP5-OCgF4iW+>P ze}FK0VHeHUtD_(HL18x;hxKsKrRtI$rHm%2^nQAvQI<4wuLuhs=jHM`L0Wy!jEKN@ z+l_ehs2wJ@OTW{;il!8JlY@v$KMf|kOT%dPLY`iK zBK;`m{cldAcr2agwpVdln5t5o1ctYa9>O6(AD}|_58zF+S9ld&Z^3T%(5yJ$-T+*> zVU~Ct@P#3Xw}iLaD86}IOt39*j%8u?NGNfM&ToIgqAv8_I59HDc>H+B0oy95bCVbW z%W2T|?LCA0ph2<4pU;TBr6C0hhF_9%_x1E|U86u+A^ZmN4jCsYWreI-uh&Q8$r}9N zxXOWBn+++2ps^0fX=BNin999h~L+B0B zPczeQ`_rGZXu*-(+@d!Q_YZr|oN2Wd!MSGg66go7v|{z`Qt#+ImNz=suZf;$4v^y2 zb$-k0m-&ookk85%%vw@QC3)pLR|czHvPmo=n_#NKzviO0x4c_@^dp6mNA~*3gxkAv zjWaV`8%ZzXvp(*?U0jWT`Jldmua7TTh}G!?3ZFWJ=uoIIHH)9@y?W?^q@b;@9!TvZ z8+J46WQVwjB3)asYQ@|?7W*XqESjJml<6Cz@*etWw0DlxgHLcBm*v(U#!^&|bKyrA z#`@}DQiYI;0b7U~*hwA$xE~MeBHJh$`-zc5i>4Pu>LVAb6bRosvEkCpb+8$OYzxYM(o3<7Y_zNgZ|RqP6H5b*B`(u(G}UZ|C$C$lcs zP+Bbw)8rQ*x5_mV`(aXZ1h86Ji-~X&cblLVN^g=>a>7ke&m$NVJLSPh4J1UuWr4>_ zF{L5az48X;8%IZW?`j2^wZjXM)HOqccSp>l<-EY;feS?pi;2|;`j2lO+R zU_$0<)py`fwZ`B=961QLXF}%NOs+!jBCLaGAF=_zMLWy550|*1uH?awDG{1t(Xb)_`kTd;ys3n#z0-;8$e8CTZS;;^gbt4%~<=pR*jiIuFUk$_Al zicq3(~Q7n-1yVW+QEHd%Vtx6!_t>E5PUx@k{^=c)N?v<0z)M#N!MWK?e9zUecJ$f`LA z2OVb&K6Icx&5Xt#+hIqIHNE^WlR6Vcvvg*$K$T!r;-ajN&q`ly*16*v8!g^uGhP~y z{BV+5^n8UvzOg`v7n$auRxOASYzrWep%kMdUn6a__SVeVKB$NwS1+t%;?3|K~+f4H!{sgex#B z=fJMUyq^U9{(%SV{M*hGV^Hw-@`6ra5S+gpteVVUVr!Kys8Ir?Z73_Qf!&D)m7CAas;0 zE@ZO{Hg6uH@+iUN#@4>cK2m>mFj$k~x;IADK7r^N>hO(g31T+!?zpr;IUOtb>hBy#LL8_? z$|^tXGj_V6Q~_7MGEOaW!4kznz8{&=dK=xfznF4Cmwcy)A7W@<&9u99(rCTxg&zWd zmz|d!!!PAaz-BraoVTJ!x}2g3ltV&fNk|=A1H0da=rM6kGLmD_bo#x#yz)(^bpIfs ZV7v{OkGRwMcVa<1R`TFM>KrjB=>NCj<2nEU literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf.json b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf.json new file mode 100644 index 00000000..195ed7c8 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf.json @@ -0,0 +1,260 @@ +{ + "profile": "AV1PROFILE_PROFILE_MAIN", + "width": 320, + "height": 240, + "frame_rate": 25, + "num_frames": 250, + "num_fragments": 250, + "md5_checksums": [ + "83dab175e49c33a6e3ece5c3758d1bf6", + "cedb4e25453dba430cb6ee830c1643f3", + "ef14d142df162819eee800f3930a9e95", + "11849ccb72cfdabc8f70e33271cd916f", + "7c9856ed61566f399a1eda2243e199ca", + "9394ed10354e986ecdfa2b753b921d2b", + "573005df5ba8f982980c1f427dfb472e", + "a96d9a913b1712f37f73b6c47b577cdf", + "91fc52b076badd1e6cbddb9bca5c5cc3", + "6253dde984bc5282f01005d7ebb55fe9", + "ad88146fe374423e7c597a922049c76c", + "ca7d1ddea7269e476b43a805bd5dd50b", + "e3d1eeec3cbfa363a2222d274890be84", + "fd7902a1b7352e04dc3940a010482a67", + "cada3ee9e33c11c99f89c3f439e2723e", + "1723ee290ae8be930f2904cb5aaf9de4", + "bf48d536e70a6a0a6549e221a833ebe4", + "f13b2e454420b1e0e5ab0adbbf2ce72f", + "31c799b4bb0971b798970e23b906cd25", + "3d925424bf645a225caf64757944a6e5", + "ac55ba724f54dd068350fd2818064dd5", + "a7125b8b0dc1e464e76ee64956897d22", + "f2a00db38d83cc778fa9fbdb3f4515e3", + "c804823f3401558a0283c4e77888af20", + "662d71a06319faef70a0981b664481d2", + "ad3a221a5f1f9d1a615733155a194385", + "8b071335d6cee4b227782415b1cb8a13", + "f3d98bfac8e5083233b88386b539b790", + "739c73e71590e64db0629911039962b4", + "0944990b0ada4012cc686b06833264ef", + "77e306874ec1b0c91668f8df0953831b", + "ca376820c5248cccb221ec8cb4b0eb9e", + "86a72cec3aaf50e393880ac8d4139921", + "b892ef1ff0c169b683b35cbaa9462ee3", + "b5feafe6c294d29adb1b05138803be36", + "3c3b150801f2dc48d300380c9a932890", + "16b06dfb6e426a1b6d88e17de83f5e2c", + "b1633b1c661a645bd1b04317b30fdbfa", + "56d08d66ad8042ba295c3f86025d44cf", + "78f6697fbb3af79dbd614f495c372031", + "afb700399aacde4c8c895def06fd8594", + "e7ae993d18af5c58047196d8369eee3e", + "65d5d77181388229606972ea99f5191a", + "bd336774b22d502d8fdca07d881d737e", + "5cabd4479d94c040f86892fa41860ca9", + "9ab949d5ede2f25889a9612b39f6f158", + "bb54bb8d7782c4b2d63f201a8d338e7d", + "9bf73fc10b4bb19021fd07586a0401d1", + "a37621cda632ac5f58e03272d226e53b", + "5c3ca20aa646d72b69f6e56592fd2f0b", + "b8ff47ec622ce73e70cfc913a3f8116e", + "1d020f74b9d3adf17b3f3da6992fcb52", + "0236c4d4a9aa68ba59eeaeb6b010db26", + "78a8e16aebbe5fc3d1e6051153031a51", + "78a54c1182b8ce007a2c10d17915272d", + "c7ac4f87168bbddd01155f54c79bc132", + "e074be8ece629e08d6d93029b8d34ce2", + "edfba91c624f4cdc558c693b33165542", + "f3d005151909922bfed7a905693de7c0", + "80118fa45af8b3e486928aae49d85b94", + "e7663b97fc9b26348876d5fa64a54d5d", + "69200a2a34bd4beccf4d473b4a4976f3", + "82f6c63a1d87037c08fba3bf16ef9bf8", + "6e3033f25eb56670a3c45c12822f340c", + "6085ac2c582fd3048c240d3dc46e3455", + "b4bbf1a7ae3f57b1d8f0ed65070e010b", + "e7f0aad67af5c7719eb89e457cefe6b4", + "6d5fdc1d68e136ce9095696be59b617b", + "bf15759321de7b21ec3c1bd2cb98ca69", + "fc4d67815a94c2e18e7d8b0b7d866641", + "0d96425a800252d500fd25da7dccb94d", + "5438c9e83cc90f030ee9b1876ec28bdc", + "a88915fd11ad765299cef49c8e65e8ab", + "4995a3442fa74849f3c45888037f9b42", + "82693761e531127d2a98cdc5615cdb92", + "07b9601bebc32dc33fd806f90cee586f", + "d68df0d6b1dae0e4428a17717723dbe0", + "2851422da5bcceebf57ae37a02a1fb57", + "ae9ff5d20ea6ba6105fb5ed3e0109834", + "a0f956663545e54acbaecf684d0841ff", + "23589649e474e155583e6742fa79098c", + "9031fbd94bd7a0508b48f8081018d447", + "b72920bce007e8f0584d615529dda39f", + "0638d4c62dbf43087b4831f2e39314fe", + "7d34a23c9b78a61da7a60c520784e204", + "0a3cf1af9095a7cb2787cf79ed257c59", + "1c05ab5abe793c14ee53ea80e170d6cc", + "02d0b03fe10b1b6675b86390fe317778", + "7639df2f62c4f958a90337b19a591530", + "6080ee3519bd5dd88fbb4050459c126b", + "75ce7282839cda34f7ca6111d6353cdb", + "2214093ad2bb512a223808ed78fe3369", + "eb9bfad6ef08a5f28b531203db3fcb43", + "cf4fd68dd57df907f35cc55fe5930fb8", + "0aaf799fc8509acd1168cd667f789c9c", + "e36ebe2332eb5a9a0ddd03469493d3cb", + "49ec5c818b0fcc02fbe3f5636417a2da", + "8430b81f1a90c325d116cec424d3c32c", + "c67e1e1f7fec9f9ab688f1d54cf2f4d4", + "fd98fabb3db5dc2d841dc50af7395130", + "8af0c8f486057cbae86939fa248c8238", + "e1888b1f32d2991682261f28f860cb27", + "9c0455744df622675a6d5232d790f5b1", + "acc73e6665f54bdaa98acead49be5f65", + "5c6e6167e6c05411a22c348a2c807fc5", + "89d09cb95dc2e212efed266b4b687b35", + "9d35702693b699ac0ccb4bffc559e2e8", + "afbeb04e4e00a1b0a4407b4a34815a63", + "6f3515d4331d83abfd1dbdfb2de78189", + "be6cb0da9149ad5d68269d0ed657cd38", + "9e9f498bc71df3097caa266089a77fba", + "40d9198beea17a254ee6b74544317a7a", + "e2f04358338ae115635e621cf229df56", + "5da22d2d7c5c304fc6198f809206de1d", + "4951c1ad5ea945a098c6a4d7fad163fa", + "5a25300d1ef28b60f9e137b47b2e4364", + "5954dd49f459ea4c0984dba30076db89", + "820a838b57e43271a55f2f17c3e0676f", + "b486477fe004ed8ee213afe8ecdb35c6", + "0715a96ada76fe2d713cdfc2b9abf11d", + "c30544fc39df1c7876043115ceed355f", + "1f7ce18debf0e339db20c68c1f0bf8b8", + "e10229f1f8a95d2e59a086e5d6ac4faa", + "8ad6c73b38d32fd1712a1fd67750d364", + "30fdf5b06ae3fd2c0df9e631ea1e1048", + "0026b1e7db5e25651ecaaf19685b2dc2", + "0b53f3f2ca17d00138b4678580d63ab6", + "cb08de2030a42826ef573082188d3614", + "e14242c776b3a32da936d1f6484e29ba", + "41a43d3ee9021bcda09efa1b218cc65a", + "9b884d243e8b330edb8f569e416960f1", + "5737c4f611cdc9871df094ba51a6a6d5", + "9d47768c1a15c0b20d28545ad5e6ca53", + "8c981af4e6c3432a330b6d8e330bc03a", + "195910783821f51cb3dad54f651c1a75", + "659833dc03dc5f929eb140e188f2b1f6", + "f2f30eaa72c5a093d67cebfda15fac73", + "3c521e3025340933cf826027a994114b", + "469b72a8db2ac77c70a792c8035b0288", + "e2cf1d7930d44341c5c363b0ceaa6c84", + "469d4283cca04e78ac2cb9407531db67", + "02416a6c77174796ecbd61adab5177b9", + "f89077b3dfb54119157e04dff24b79bd", + "2d1a8fc14b649e343288b5972abe1fdf", + "3d059186dab49cd8d13584dc365862db", + "765ef42f457dee16234df6992ec74cd2", + "957589c357d82631477177e6250c713c", + "2473af8395d08e9396216c082f854d6f", + "6a117d16f8e8ffe87aebec05844d6a0a", + "8e467cfcbd66ed80fca648248af56b3f", + "71e39c4304e09688750aa85e9d040f4a", + "3071ee85d87ef8029619d4777c799c2b", + "cc86f09e3f228001e806fd3afe5e1271", + "3828673cf67828240005f7bf9fe37412", + "12700b04cc22fba7688ae1709e48b886", + "a1045aa13b0007d23f22ad8e83c4bdb0", + "a12e6c2d2805336c407417b7503670fe", + "11dd1907c6785eccbedeba6a5708c516", + "3fb8be1276b83db1ccd8206cc69ec736", + "980ce03ae4e6f2eceb314f37290e0dd2", + "c39395d9b6108f1bd4ab1f004c44a5f0", + "a7244c89efd3b00611c92ebaf98793e8", + "fbede5a2957c023216e79cc31ab32946", + "bb40544a3adfe992e07a8eb3e5bf6966", + "0f71f57100699e95fff51dabde86cb42", + "6ba085efefacf385728423ca99bd4629", + "4df2d956234cb2129ebefdd90f28185a", + "d0a0d4263ef4f32d59b004752a5b94af", + "a9deed8bf550f4ee370730e1d993d4dc", + "eca65ba94930bf78854ce991c05e3d9c", + "4eef9fa5bca7ee74e97a0ac6e12e5f53", + "84e97d688ba1922f9ed71d72cea67259", + "407dc974664f93be14c53a2b291cc422", + "31eb9e589470cfbe4e72e1c441d56e3f", + "ea4f696ad2ae150a1f0e622874f67fca", + "7552fea18e053b4771c0490015488291", + "6863cd478581244fff72f6a0021a9fb8", + "9118deacdd49db27c317eeec868cd870", + "50290c87c5ee1c558bd304fc0f4a15ea", + "5aef6d78538151648450fd391c9deaec", + "a7e3feb23e01e0b4556446e01b2d81eb", + "6918645d2b0bd511247c6ed372bf213e", + "2f9661df4d114ab9f3a98a255a8177f2", + "baaea6f9f05a7a2bc10721f1afe6f57b", + "366ab99c236a8e84e658bad69d82a1ba", + "f950a6508c051ef32e4c04be56fcc719", + "9e586c73c3b9daebc608600efbee33b0", + "e9fb7690077e8cbac15d5564130a8d7c", + "ef3c8d2b5376753c26a795bf403cdcc1", + "be46eec37b67dfc8077705ce89588a7c", + "dba1b08cfeddbcf4e8d79621ee343bd4", + "a4a612bcb6c33c7799433c998c5f12d1", + "5f45fa214bc860825510d034f1ff26de", + "1766dd6fe6080c408a4baca135609f1c", + "34dc16d50d7afeb93a874563f1f5e4fd", + "0a5314f217972a9271c4792921f61153", + "7c228f1f5ccc6f7307526531f79a307d", + "188544479f5a44b6a90b6be62e33611a", + "9cb9be5574c6a2c06862a3ed605c68b5", + "845b033cc82472d8e647f2a3bb3eb653", + "2c6b66c68107fd13395a1ed5f0639355", + "0436424e24e562c52ab7e7063b04c129", + "21248608746bb252faa347028f5343d7", + "81607476ae05bbf7386dea9d8eac352b", + "fadc83171e49e779ebe5d81769049cef", + "e768c96238a8cf14caf8d03420981265", + "c57aa4d8e2f7c9101868246de4de1c13", + "b405a01f8eb1d4ffade0e49f593e5a8f", + "76f02bab5b932bf4bcf4ce06bdf0e42c", + "51b09ce7117e15cf654ad76e13263f0c", + "45f650b845e31b87b522574cc7afde80", + "858605e70124ef7130742b713b079ad9", + "5d955329a007742ecab7153d2e69b262", + "c4139450cb6ffcac62b764d62083bc31", + "8b13de43cffa8d12dd17b8e749a375b2", + "b000fbdb48be876302bc336aeb6deba1", + "88e2b7abbc33f70231889113582f8f8c", + "d9f31d4cfeee31da719b3567d5d11a19", + "7c2a6cc9be9ce8cb96b5a1616909941b", + "954c2be78496f0235ab137a98a5ae11a", + "9efd13fe15e8900f67a6c76103c9ce78", + "b04aedbee4993ff955ebba1dcb8a04e1", + "83b045a2a43445f97d82c1faaf33b332", + "1cf416bf7bf4a2124932d4b1bb0aca94", + "ac17d329b587b065512269fa3def8279", + "fcf2b7ab8b7d53741c737f75c3587a6f", + "0aaaaca0aa165a6786e2161043aa1e72", + "8d19a0848fad7b32a15522cc0b24b4d1", + "be0a1be3f90ff47b80be31be021c28f1", + "c9e423c59186d93100c56ad8f14c0fb4", + "052dbb80693750c716acce06f3ad7266", + "6a2c8002df49d3832b7e1abd70af542f", + "92bec3aa91edfd2cc5dee7d1f66ac189", + "579d29110e7a7a71152aedb9a462c79f", + "b0bb435df9dd211dd5da3c547c8887b8", + "fb7a3cf05afd06668287f5dcc9c20a55", + "bbd27b9b555f361478f9516e839077a5", + "a74dc95fe982709b25434bcc53a91973", + "27f82418568440caea8df7bcdf56eae7", + "d5ff54f75c8fa6d60428816945ee5e89", + "4b6677c4cc9866f477ecda5618411c8d", + "d56046f25113a82b090bf9a91291587c", + "cb56aa0f4343c7da821c04e55e8af21a", + "c633ea8a4c20ea4d70c8c241d0293e9b", + "dd2b157ce8a010a61908170ecb3e31b0", + "3c3509c92b03a702ac1f4a28b5abd3d5", + "d1400e54b8b61241cabad5291d6e3a43", + "c29470cd6afb0aee4ae4a8d0622a468d", + "e35764f7bd48746d37478b2d6f3f2755", + "3dcf1fd38fbcc1d45f98574b17ac5710" + ] +} diff --git a/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1 b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1 new file mode 100644 index 0000000000000000000000000000000000000000..83b53b2710bcac516081e7bbfe0f2e7a755475ed GIT binary patch literal 223686 zcmV)GK)%03OG!om03ZNCRxmL@0q_7B000010002`000000001YDF6Tf000000000I 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

1SA13Uv$S0}t4Y(!=*&KXY}Ht?<|(h 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%0D0jgEYIqk87i^VBT**g}3+hw$2*E(f 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 ZV7v{OkGRwMcVa<1R`TFM>KrjB=>NCj<2nEU literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.crc b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.crc new file mode 100644 index 00000000..43013f62 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.crc @@ -0,0 +1,250 @@ +6aea6152 +cb4a90b3 +fd83e35f +074bd081 +216fcc04 +c73ca1e4 +fbfb2a30 +cd587935 +e8bc2912 +051517b7 +f3e9831d +812e7bba +3e2054e6 +7446385e +8b75d043 +f930d9a7 +7bf6b591 +253c5389 +11a25f1f +c5101d08 +ee1c0aae +ce055a9f +0ed4a046 +aa0a72c3 +87f7a598 +aa3a422e +0ee0e533 +5ce3d683 +cc7e88e5 +3e1c7774 +bd6708d3 +0ba6a0ee +ac00d2da +f1222e65 +3c6aadc8 +a28e7327 +fd2d0d8c +e0a791bf +90a33d7e +63ae28ce +26230e31 +215e9021 +6ca1295b +01604116 +15f8d3c9 +6f28a1d4 +8d54633d +08cccd41 +0b082923 +0aaae5a1 +98310e98 +33f12578 +9fd072e0 +36a1220e +a278c894 +828dcf3f +3573505c +861874b7 +dd78b1fd +310ca9a0 +74d670f3 +93c48c50 +814991b1 +fca836a7 +9299e121 +a8d3b267 +661a1c05 +50038462 +bc866fe1 +46e2d826 +7035f78c +00c2ed17 +6b64a0c6 +315f4761 +df4c3ef7 +7ad4996a +3185972f +19af5f17 +01c4e3a3 +3728bd7e +16adc384 +5798878c +d0ac34ee +56f76f8a +2b6f3f47 +4a5ec1a9 +75f0027c +7a1dd1f6 +50c0f14f +978d00e7 +a1c430b8 +7c67432a +4615b4cb +c53c7308 +876d9f98 +b0015dd1 +73288a1e +d843f40a +4127f309 +3adbe013 +9e84104c +9f98680d +380a36e2 +2202f414 +ca931e02 +182b20e1 +815a6be2 +59cc5d53 +0a05d420 +d53b5f2d +98c613aa +e4de4fc1 +2b8f6d29 +48ce9586 +d6e822dd +c8674577 +75dfa0e9 +7ad6ed39 +f7725767 +a44e0f11 +bcd743cd +1ccf1835 +8ceb6153 +0a43af09 +79bab263 +855dd65a +a37d34fe +be063b4d +aec5df0c +7b12399b +c5ad7294 +2f609050 +8bb2dcba +c19aa763 +700eb99b +11bf5174 +e74d5eda +f54e54ef +0173e328 +162372d6 +3c8f18a3 +ce8579c7 +91bdbf86 +745edb27 +df783664 +e7462cfd +30d8d761 +f15474e6 +4c5bb874 +27c36dd2 +c40981c0 +1cc4b7c0 +ebd96a40 +a5f4d31e +cd1a957a +0b0d8e1d +c5d30b99 +d7d6b75e +0ea512b8 +dd12fced +29eab68d +e4bb47d1 +e3060770 +0483e6ca +296b084f +d11abff6 +c7589394 +b187d62e +2e217b3c +b516603b +287c8b71 +2f24a994 +7ea617d0 +7ba0024e +7067ae3e +95650cbd +7b2e7deb +d1b6da6d +76f22a5a +9ce6fac7 +1e5cf6c7 +bbe20b47 +2f2cc450 +3f0297f3 +a2084503 +06487daa +5d06ae48 +02cbba1b +b386b4f4 +47623370 +1acb6e9d +5b55fd00 +70d43ae3 +8a0703c0 +4d62a79b +e358a27d +b89fcec0 +59508160 +f2c9f57d +2b57ec09 +9427c163 +91169e76 +eda7cda1 +289c7bc0 +821dc9d5 +b003d6ce +3d8dbc53 +d6c3324a +76a7b9c9 +a4d4b21b +51ce1f2c +49cc8861 +b4fc2309 +c435862e +3bceba0f +7ee0c219 +cb04facd +b2fad23b +2b8cf4f1 +fcdcbad2 +f3c038c6 +71e639e9 +282325bf +52c64dc8 +9ebe8705 +f8679dc3 +906116a2 +9f740960 +3cad2af8 +c7765bb4 +c8f38650 +1e5d7d36 +3374dc4c +e83476e5 +6a9c0d00 +08b8cb58 +bd74ea9e +399bce4c +1577335d +cdbbea48 +d1373c35 +cc62212a +8a5e59e2 +75f5f297 +8c5f3776 +fce09105 +92b2d17e +416e8530 +968d1450 +d20688a8 diff --git a/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.md5 b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.md5 new file mode 100644 index 00000000..ad04cf61 --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.md5 @@ -0,0 +1,250 @@ +ce5386b1beaa89f73a668d3720717fb4 +8f5d21bf3d78d9c242609bdd8df6cfd6 +7d75a95c82171c8cf4b09d694d4cbef9 +20d4bebdc51702ee7f40e187747696c7 +9c55dc575db2a189c5576537afca2c54 +68a15a8cdbc0aeaf10dff65db4d1a810 +dfd03584b10852eb16901d52578d8f8e +a9a0cffed57bba344c9366ce91408e26 +9fdf7ac0fc07f2391da56462faf3e2e0 +b83d2da8f82e1a06d26bbb514b471f4b +17c92a58fb68d0b3b02cb5b9105dbb37 +1e5bd3c05fb67f2aefc01611ed43d117 +eb287667a027f3e5325bfb27652021a6 +67e388816af98b99e4b9134516205c52 +35cba13cd8a6f23d6dfd95803af0886b +45966e4a1b83dce6a2b0c259fea10cae +416a95236e46a7bb0f11cf1147631979 +f23ab59d3c4efbb8eb2d80d67a2e5136 +ab7bce374a3468c03483418ccffb52de +bdaaf22c6c4e58d393d7c051d4d0ae8a +6f7de2de3e8d79c726f0f2c2fe52ee58 +d2fc7067797bbffe3765a238ff803b85 +769dbc781b43759287da0b2888e8338b +2abd608af57978290e24c4cd62e4904f +4fb54d7b0a71a4d3ca95b56d7b3d9a81 +262ba91321a0edac67c01e6ddcc2e4f7 +4c9c2d967fb853ecdc6042f223a7d138 +f21ccd6a4eb5f561b0a0eff9cb1956ec +3c206ed277864249e7eaade75e71c815 +7167fed00621472662b19a0215dd00c8 +76e6f142e710ff5d7f6797d3fc1b8dfd +4d814bb1fec0a2bca9d92140d5734ae0 +65b576bf469b182c8954591fd15dd21c +abb7e8f11617aa0c9bf6870e537b008d +02b86ea6b2f8699097d6899ab1f073c4 +610786c84b9fc118679afb09404c15ae +7feb8fae65767f848ebbefa730f15d68 +12677768a4266571e85b750b51ef9fc8 +5b6541b178f6a885d0439b381e501967 +352cc90ecc1f3e26c19970f106c475fa +9c5215a9fce00f0ec30615649171f8e4 +27e593a4b30c0b64939c62e5e0488e5a +d8a5fd54958527ecefc642f6730e4164 +754dfeb4337108e534ba70bab429777a +821ad11c37ffda3ad5438b57a9523e9f +add12a889a5e08a5358e2e0e086fe522 +fa100278f49cbb7f9c9d7dc5e12fc0e1 +ef46a151df454084d0f0bc644adeb47d +c02f8023a4be8a3249bcf6e1b223dffa +41a11da85806e85fa86a9de41e1cd93f +e792d89b5df357d1b03b1812a762832c +90f72aead8124bc6ce51495b4b99da75 +af25c00b1990b778b50f529ae81efa30 +900f66f0e222a8d253153711a23a5ee9 +0850cad30d59e1b5e5e2943e6b916d0a +03ca961e9876ee96b14711c44a9f2a69 +fa6bf9c30dd8618fdab499e9dd217582 +4e09e08e1f99bf210d4757281ab4c836 +dbf2bc5355a6846a028a7b3140fdef34 +b38e1973cabab58f8137cf66fc7fa3aa +0ee4998cda155ca2823f5c9336a62cc2 +4f9918b4782a59a0361619389bd22d54 +c5c82a59505a9888b95e012a068b3254 +5eabc71dca6f57407ba306db416c5031 +41b8b1155bf6144aeb822acbff051e1a +12e7d40ccd4298400cf3b5577370bff8 +2f22b7979183c9c3b26838c7e84a49a8 +68af2e17a487001d9374f6b38ed950af +3a55bc482844ecc366e10544c38cc890 +6a08d19bbecb554837c9922b9818455a +b2f95f48c1214a5dfb8f4528afcbf901 +c3c4fc391a1a040c71cb65a8c654a5b1 +86786c55d138bb9a4a4165522cd15253 +b329ece66a9dab6bebab9e3687629e7c +7d6d818e7fe9a9b335c72ee6aedf9980 +317956c14cb119eda584de03dbf089cc +d095660e8dd16899be9e916e0ff7946c +12571880bd56bed1bbbd034094fabbaa +7f702375e8c43036e36ddc07b543afb2 +e254d4c9854b28db694fb5477144551e +24a4b250bc0461ad4894334838fbb645 +16b3dca0bfcad463498a62bf8a3f06a8 +77dbe27a373d87a6fc1012caa166a0a5 +1b304ea04baf90e1bf11d6296f64f933 +42c94efb8ab8ceb6ff98497ef0981c66 +1074398b6c210e833b4135c7d2caeb91 +3bce8712a7ba7b037a2cc85e4f72aea4 +42c1bc84350e82cbead374b41732d8d5 +76f8fc8e26d1b64e8afb8f263ec3f795 +ef9f8b042eb888f5408ccb500c9a157f +d46505a331887bf49e157f8de4c17a98 +0be7df49394337bb277b63a6b9147520 +deb977415048b0b7fa1fd02878c1b10e +a0395e32b4cc279f8545b791cee5d394 +c7dcd8741840799886a1f3b5208c0b36 +02b1297f58b9e699f232b9ed7e8bbcd0 +48852b209e405d7aaab50c5810448579 +d8a5598b2e7183d72cd3a81e54f301ce +dfe2e4af7518f0d1f04fd825069d575b +e1a70679c95173e6a1504b7c52dd0d3c +16ae515dfb1268052bfe330a1cbbbffa +f17c090f2140cfd3060558ceefe4d1bd +58b1ba1fbbd76670c9218a846a8f6baf +f543d8380c2d095f9b62d4ffaaee7467 +1b16d3e1d688a5a905ff22680ad803cb +12e16d58422d5a5899379f11c6ca5b9a +f1986c3e4a4545356656f76c27db0ae7 +324a3d45f631aed6c758888254ae07ad +49532c06798f685e2a41dc683b081f79 +95934c5a6e2cc8de08c8b8342936cf38 +67d89790392f18908c24cc36bbe50362 +c4f963cd490f9241f4959cca6a6d35ce +b21db693ba10898ec3205cb0cb343a02 +72e63227e74f2473a44da365fa9bc02f +220d247b6c0f9512256dfcdf95582148 +86c290279ffa61ca615f1f8f8877e807 +6651d56c548fd4c5e8b2c86354c28ca3 +d863321ec53cd582d34a8ee5e88a5576 +f262bda775cf67026d8d226620c9d4aa +15816d939647890021ac31aa1c3b2f75 +229b295a4bc44d8522580dd4383812d0 +833afe87641e34e60f2d127f44a7cbeb +62cc76ec3a853878ebfff04ebbc8addc +40fca93e2d6a976de641cdbfb56257b5 +65d654e9145527f8fcd24608d64f68f1 +1b2b0c68b6d9305f46642109b92a234b +85aa859b29f2caeb60343cf53718e451 +3aa590c6c8a4dee128f1e7fcb2b82a2a +699cb26687934746b722e2dfbc1d2c82 +9cc9b7f51cbdbaf15bc8eec9c1a1cf7c +b1be900fe6a6eb21a563754420c5f38e +47976686aff65b42661ae0fb2685f462 +6b3f96a1be97c4c2a371d5935672096a +ac9b60c754c95ccbaf56f68544712dd6 +7dab91b68788d2ed61cd40d3073091a8 +35b367c591e71f11ddcd9dcc75be79e7 +d3321d793265630e81e6d52b20e58f44 +9d97047182efd2ba0594a569f6f2ce04 +14c772a342a28bd6785bb722e67fc767 +288802e107c373c9d28eeb3f8726214e +1c9d8ff04c346a59ea4f3a349dc4b891 +2c857a65df4619d06f424ddcccbd2221 +10a0b3e4e7825d21fc22bcff34a3b26c +78c8a5ee6e6880a3af222bfc27de62a1 +9e16c4346a69ff9127bb6c9287d98859 +1f1d95fa30c9c3a58f41d9657aa0e6ec +5e8c6b3b4690da7f4175abe288d0c509 +0bf315da2ec1e135963cc4c9cf73ea1a +2992fdf295438ec9f8dc7979b6d4e5ff +a3d43afab0119aa3b6e38a81d244c388 +889d049dc64457637ad089e18e718a23 +46016c611b9323fe8f4206bd393a6741 +f555b79be75ba2a1a741f467192b693e +242d4b4c323c7c1e9648de52ec8d7c2c +383d2ee4d28919f193f28448ba28ddc6 +2c7cd221bd1025547fc654ca6717553d +e16de72e05b548758832102ffb39d242 +5cecf3fc2823225f0f1748a1fedadf35 +4e709cb904317a43ffd94739281d32bc +b6dfd4657668ed2c1ebaff9fa92702ab +3c618cd95706cc25a0565fc0ed00e352 +dca8dbc91708a94525f16c064454a2ce +fe3fbff45e30dcc3fa4f57ae78114f7f +0f7bc1eab5cf9faeaa127c0243675253 +19b6fca6e3f0d039074753265436d2c5 +fce950fc2ec2526c0b29b6869ff58dee +4c9c0b19668dc20a9fac84e13c590deb +8fe06eb38ee870ffd7cb9f065476ac78 +515ce9eaa9a6970f809475e416325123 +6d58868e827dd9dca698c5b24380fea7 +072951a0e1d06aa829d716a86ae498fe +07f23add152c44f754eac930093e6b59 +e98798eb29c162ddfff7ec72aa7b65dc +62f4e1ca0294a3944224fb4a4280ed56 +28fc910d3b7a926f63e7bfaf18878cc1 +40065fe5c0e32a16622988bc1485a038 +b2623ec75309e28c6c0a4b179e4acd6d +3ea6b991014bde2185be7a6c92745c28 +5fde25c7fe79a9c0e2b86eb1878de02c +5bf42dc3006f4c601d01ee4820e44d38 +44500b37f18b2eb499894bc1ff1efdcc +f5bfe3352c4281083083e51ef1fe0a72 +abf9c6ba639f68d9b8f8c0d9d97a19d3 +75f6dc9ccbc6a1381785bf9440a95744 +4db0fd27cac20f59718fe00c5dc13202 +9e0d611017698f2d1716f8d9d81a8bf7 +13c7015f10b37b3dbe1618cde5edc2ee +70e2a0182471290a46961a0d62611053 +47ba942fc3396104b3731ea40eb97339 +bf10238458969233c0062b09a68f22a7 +115b7375a9aa156b8203dd88acdc5672 +1b39aa22702c993aa1e7606e9afad9c5 +c85e67c08e02538f151c6276662ba69b +89dcf41c5adb944bcd46ca3f67666a75 +90fd81f9b572ede873313f4faa58da4b +00badd56e7d5b2dddb10ef7de05be515 +a13c3404ac033c9fce8a6435f2a4a416 +af83e8f7af9785c96ff61c354417aab4 +5176b27945cc7686520b1425144dc4d3 +e5f248be2c0e83ad0a6bfd2eff759f6e +cd693cce87898dafa5c68dbaeeb151fa +2a47245fc3da1229e3dc288f216c4e20 +c6f8e88fbf5566000f8f86b0d4bf9020 +3e88c5f7b571a292c998cac33b41215b +2a2e8271ecba2cec040ef343317e2ae9 +8e693282602bde3452061affde3ce514 +992acab07cf4d6aa5b95b4c4cc33cf0c +6db1ad3494be9c3c9758b00d956bd2cb +824d130a16d7bc2c6d6b26c31c2a0e05 +d286bd8f5cdfd65b695815ff68bb191f +4c5bbeecda37bf4fb6dd79f266aac3d6 +c4f64e991e13dfc3913246f730c31e87 +459d8fd53975479aa977c61dc3ef1b9e +ece59558539b52684433b54bf2c7e214 +4362122d7ed40dd5ad0886cdf901539e +66ffe4fb31a5e4fa9462a3aa57224476 +86586a04e75ebde21903f407e6037e50 +93b2e3af91cb24ffafa6418c76cc1ce7 +41ca4598aac764fa944d8f578b2ab5ab +6ef7ce05836d63520925817c220a5274 +c4e902503f3d5602ba2454907d7809f7 +8f3cb447682bebd3a230740e11d35dff +4340ec4c3e62118b2749264393fc78ee +8178fbeab83126f2111ab74c23d241c9 +db298bcb2a999b7ac2037991ef078d32 +f586895d6dca606e2d62dfdc92cc2021 +b5aaf4b91dd7b6cda3a2635c603c4500 +0f024b59afa957b59f5ebb9b63ff1b1a +41acc378b08b5d9b7da2f644b79aebdf +cd173062829e6e86eb094b8d40d16579 +ef55ddf0fa94dcb4f275033bb55e8466 +48a3efa1f9d8291d99ce434fc5474fb6 +731a649c764fa964d6e2ce640b34c25a +5f9ac7b1dca56c8dab9ce1f92341094b +e9b0ee7355299aaf55cce1e240050125 +8a285fc7c728cdfa1f4b651c64eab7e5 +05823b86e463ad514a4cd7eacd9c01bb +0ce34318119eb0e8921b5e798789bea0 +e46f4c793b42136b1eefbece7b867052 +1331c7fcae627f290299b978299a9b48 +285c5783029a7b23374e91cce19df0de +670fba4fad181308c15778f38e619646 +46a3350a39c21f45bafed75d339ed101 +b1d6f3230e58d2647d1aa4a5446061ed +f6eb5936ec9bb3212b14716da8329839 +1ecb00343900a550ad33ba10777df23d +4b9f70244adda8e0a42f4da7341b8d84 +6caafeb26793a011ebef352ef1d1aa34 +723ab0797281e7ee36109b53140a324d +7a5e9be8c7c8307aa22c8b48708424a2 diff --git a/vendor/cros-codecs/src/codec/av1/writer.rs b/vendor/cros-codecs/src/codec/av1/writer.rs new file mode 100644 index 00000000..96cd32fc --- /dev/null +++ b/vendor/cros-codecs/src/codec/av1/writer.rs @@ -0,0 +1,199 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::fmt; +use std::io::Write; + +use crate::bitstream_utils::BitWriter; +use crate::bitstream_utils::BitWriterError; + +#[derive(Debug)] +pub enum ObuWriterError { + BitWriterError(BitWriterError), + UnalignedLeb128, +} + +impl fmt::Display for ObuWriterError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ObuWriterError::BitWriterError(x) => write!(f, "{}", x.to_string()), + ObuWriterError::UnalignedLeb128 => { + write!(f, "attempted to write leb128 on unaligned position") + } + } + } +} + +impl From for ObuWriterError { + fn from(err: BitWriterError) -> Self { + ObuWriterError::BitWriterError(err) + } +} + +pub type ObuWriterResult = std::result::Result; + +pub struct ObuWriter(BitWriter); + +impl ObuWriter { + pub fn new(writer: W) -> Self { + Self(BitWriter::new(writer)) + } + + /// Writes fixed bit size integer. Corresponds to `f(n)` in AV1 spec defined in 4.10.2. + pub fn write_f>(&mut self, bits: usize, value: T) -> ObuWriterResult { + self.0.write_f(bits, value).map_err(ObuWriterError::BitWriterError) + } + + /// Writes variable length unsigned n-bit number. Corresponds to `uvlc()` in AV1 spec + /// defined in 4.10.3. + pub fn write_uvlc>(&mut self, value: T) -> ObuWriterResult { + let value: u32 = value.into(); + if value == u32::MAX { + return self.write_f(32, 0u32); + } + + let value = value + 1; + let leading_zeros = (32 - value.leading_zeros()) as usize; + + Ok(self.write_f(leading_zeros - 1, 0u32)? + self.write_f(leading_zeros, value)?) + } + + /// Writes unsigned little-endian n-byte integer. Corresponds to `le(n)` in AV1 spec + /// defined in 4.10.4. + pub fn write_le>(&mut self, n: usize, value: T) -> ObuWriterResult { + let value: u32 = value.into(); + let mut value = value.to_le(); + + for _ in 0..n { + self.write_f(4, value & 0xff)?; + value >>= 8; + } + + Ok(n) + } + + /// Writes unsigned integer represented by a variable number of little-endian bytes. + /// Corresponds to `leb128()` in AV1 spec defined in 4.10.4. + /// + /// Note: Despite the name, the AV1 4.10.4 limits the value to [`u32::MAX`] = (1 << 32) - 1. + pub fn write_leb128>( + &mut self, + value: T, + min_bytes: usize, + ) -> ObuWriterResult { + if !self.aligned() { + return Err(ObuWriterError::UnalignedLeb128); + } + + let value: u32 = value.into(); + let mut value: u32 = value.to_le(); + let mut bytes = 0; + + for _ in 0..8 { + bytes += 1; + + if value >= 0x7f || bytes < min_bytes { + self.write_f(8, 0x80 | (value & 0x7f))?; + value >>= 7; + } else { + self.write_f(8, value & 0x7f)?; + break; + } + } + + assert!(value < 0x7f); + + Ok(bytes) + } + + pub fn write_su>(&mut self, bits: usize, value: T) -> ObuWriterResult { + let mut value: i32 = value.into(); + if value < 0 { + value += 1 << bits; + } + + assert!(value >= 0); + self.write_f(bits, value.unsigned_abs()) + } + + pub fn aligned(&self) -> bool { + !self.0.has_data_pending() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::codec::av1::reader::Reader; + + const TEST_VECTOR: &[u32] = &[ + // some random test values + u32::MAX, + 1, + 2, + 3, + 4, + 10, + 20, + 7312, + 8832, + 10123, + 47457, + 21390213, + u32::MIN, + u32::MAX - 1, + ]; + + #[test] + fn test_uvlc() { + for &value in TEST_VECTOR { + let mut buf = Vec::::new(); + + ObuWriter::new(&mut buf).write_uvlc(value).unwrap(); + + if value == u32::MAX { + // force stop uvlc + buf.push(0x80); + } + + let read = Reader::new(&buf).read_uvlc().unwrap(); + + assert_eq!(read, value, "failed testing {}", value); + } + } + + #[test] + fn test_leb128() { + for &value in TEST_VECTOR { + let mut buf = Vec::::new(); + + ObuWriter::new(&mut buf).write_leb128(value, 0).unwrap(); + let read = Reader::new(&buf).read_leb128().unwrap(); + + assert_eq!(read, value, "failed testing {}", value); + } + } + + #[test] + fn test_su() { + let vector = + TEST_VECTOR.iter().map(|e| *e as i32).chain(TEST_VECTOR.iter().map(|e| -(*e as i32))); + + for value in vector { + let bits = 32 - value.abs().leading_zeros() as usize + 1; // For sign + if bits >= 32 { + // Skip too big nubmers + continue; + } + + let mut buf = Vec::::new(); + + ObuWriter::new(&mut buf).write_su(bits, value).unwrap(); + + let read = Reader::new(&buf).read_su(bits as usize).unwrap(); + + assert_eq!(read, value, "failed testing {}", value); + } + } +} diff --git a/vendor/cros-codecs/src/codec/h264.rs b/vendor/cros-codecs/src/codec/h264.rs new file mode 100644 index 00000000..fc13025e --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264.rs @@ -0,0 +1,10 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +pub mod dpb; +pub mod nalu; +pub mod nalu_writer; +pub mod parser; +pub mod picture; +pub mod synthesizer; diff --git a/vendor/cros-codecs/src/codec/h264/dpb.rs b/vendor/cros-codecs/src/codec/h264/dpb.rs new file mode 100644 index 00000000..00bba0ac --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/dpb.rs @@ -0,0 +1,1243 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::cell::Ref; +use std::cell::RefMut; +use std::fmt; +use std::rc::Rc; + +use log::debug; + +use crate::codec::h264::parser::MaxLongTermFrameIdx; +use crate::codec::h264::parser::RefPicMarkingInner; +use crate::codec::h264::parser::Sps; +use crate::codec::h264::picture::Field; +use crate::codec::h264::picture::FieldRank; +use crate::codec::h264::picture::IsIdr; +use crate::codec::h264::picture::PictureData; +use crate::codec::h264::picture::RcPictureData; +use crate::codec::h264::picture::Reference; + +pub type DpbPicRefList<'a, H> = Vec<&'a DpbEntry>; + +/// All the reference picture lists used to decode a picture. +#[derive(Default)] +pub struct ReferencePicLists { + /// Reference picture list for P slices. Retains the same meaning as in the + /// specification. Points into the pictures stored in the DPB. Derived once + /// per picture. + pub ref_pic_list_p0: Vec, + /// Reference picture list 0 for B slices. Retains the same meaning as in + /// the specification. Points into the pictures stored in the DPB. Derived + /// once per picture. + pub ref_pic_list_b0: Vec, + /// Reference picture list 1 for B slices. Retains the same meaning as in + /// the specification. Points into the pictures stored in the DPB. Derived + /// once per picture. + pub ref_pic_list_b1: Vec, +} + +/// A single entry in the DPB. +#[derive(Clone)] +pub struct DpbEntry { + /// `PictureData` of the frame in this entry. + pub pic: RcPictureData, + /// Reference to the decoded frame, ensuring that it doesn't get reused while in the DPB. + pub reference: Option, + /// Decoded frame promise. It will be set when the frame enters the DPB, and taken during the + /// bump process. + pub decoded_frame: Option, + /// Whether the picture is still waiting to be bumped and displayed. + needed_for_output: bool, +} + +impl DpbEntry { + /// Returns `true` is the entry is eligible to be bumped. + /// + /// An entry can be bumped if its `needed_for_output` flag is true and it is the first field of + /// a frame which fields are all decoded. + fn is_bumpable(&self) -> bool { + if !self.needed_for_output { + return false; + } + + let pic = self.pic.borrow(); + match pic.field { + // Progressive frames in the DPB are fully decoded. + Field::Frame => true, + // Only return the first field of fully decoded interlaced frames. + Field::Top | Field::Bottom => matches!(pic.field_rank(), FieldRank::First(..)), + } + } +} + +pub struct Dpb { + /// List of `PictureData` and backend handles to decoded pictures. + entries: Vec>, + /// The maximum number of pictures that can be stored. + max_num_pics: usize, + /// Indicates an upper bound for the number of frames buffers, in the + /// decoded picture buffer (DPB), that are required for storing frames, + /// complementary field pairs, and non-paired fields before output. It is a + /// requirement of bitstream conformance that the maximum number of frames, + /// complementary field pairs, or non-paired fields that precede any frame, + /// complementary field pair, or non-paired field in the coded video + /// sequence in decoding order and follow it in output order shall be less + /// than or equal to max_num_reorder_frames. + max_num_reorder_frames: usize, + /// Whether we're decoding in interlaced mode. Interlaced support is + /// inspired by the GStreamer implementation, in which frames are split if + /// interlaced=1. This makes reference marking easier. We also decode both + /// fields to the same frame, and this frame with both fields is outputted + /// only once. + interlaced: bool, +} + +#[derive(Debug)] +pub enum StorePictureError { + DpbIsFull, +} + +impl fmt::Display for StorePictureError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DPB is full") + } +} + +impl std::error::Error for StorePictureError {} + +#[derive(Debug)] +pub enum MmcoError { + NoShortTermPic, + ExpectedMarked, + ExpectedExisting, + UnknownMmco(u8), +} + +impl fmt::Display for MmcoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + MmcoError::NoShortTermPic => { + write!(f, "could not find ShortTerm picture to mark in the DPB") + } + MmcoError::ExpectedMarked => { + write!(f, "a ShortTerm picture was expected to be marked for MMCO=3") + } + MmcoError::ExpectedExisting => { + write!(f, "picture cannot be marked as nonexisting for MMCO=3") + } + MmcoError::UnknownMmco(x) => write!(f, "unknown MMCO: {}", x), + } + } +} + +impl std::error::Error for MmcoError {} + +impl Dpb { + /// Returns an iterator over the underlying H264 pictures stored in the + /// DPB. + fn pictures(&self) -> impl Iterator> { + self.entries.iter().map(|h| h.pic.borrow()) + } + + /// Returns a mutable iterator over the underlying H264 pictures stored in + /// the DPB. + fn pictures_mut(&mut self) -> impl Iterator> { + self.entries.iter().map(|h| h.pic.borrow_mut()) + } + + /// Returns the length of the DPB. + pub fn len(&self) -> usize { + self.entries.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get a reference to the whole DPB entries. + pub fn entries(&self) -> &Vec> { + &self.entries + } + + /// Set the DPB's limits in terms of maximum number or pictures. + pub fn set_limits(&mut self, max_num_pics: usize, max_num_reorder_frames: usize) { + self.max_num_pics = max_num_pics; + self.max_num_reorder_frames = max_num_reorder_frames; + } + + /// Get a reference to the dpb's max num pics. + pub fn max_num_pics(&self) -> usize { + self.max_num_pics + } + + // Returns the number of reference frames, counting the first field only if + // dealing with interlaced content. + pub fn num_ref_frames(&self) -> usize { + self.pictures().filter(|p| p.is_ref() && !p.is_second_field()).count() + } + + /// Get a reference to the dpb's interlaced mode. + pub fn interlaced(&self) -> bool { + self.interlaced + } + + /// Set the dpb's interlaced mode. + pub fn set_interlaced(&mut self, interlaced: bool) { + self.interlaced = interlaced; + } + + /// Find the short term reference picture with the lowest `frame_num_wrap` + /// value. + pub fn find_short_term_lowest_frame_num_wrap(&self) -> Option<&DpbEntry> { + let lowest = self + .entries + .iter() + .filter(|h| { + let p = h.pic.borrow(); + matches!(p.reference(), Reference::ShortTerm) + }) + .min_by_key(|h| { + let p = h.pic.borrow(); + p.frame_num_wrap + }); + + lowest + } + + /// Mark all pictures in the DPB as unused for reference. + pub fn mark_all_as_unused_for_ref(&mut self) { + for mut picture in self.pictures_mut() { + picture.set_reference(Reference::None, false); + } + } + + /// Remove unused pictures from the DPB. A picture is not going to be used + /// anymore if it's a) not a reference and b) not needed for output + fn remove_unused(&mut self) { + self.entries.retain(|entry| { + let pic = entry.pic.borrow(); + let discard = !pic.is_ref() && !entry.needed_for_output; + + if discard { + log::debug!("Removing unused picture {:#?}", pic); + } + + !discard + }); + } + + /// Find a short term reference picture with the given `pic_num` value. + fn find_short_term_with_pic_num_pos(&self, pic_num: i32) -> Option { + let position = self + .pictures() + .position(|p| matches!(p.reference(), Reference::ShortTerm) && p.pic_num == pic_num); + + log::debug!("find_short_term_with_pic_num: {}, found position {:?}", pic_num, position); + + position + } + + /// Find a short term reference picture with the given `pic_num` value. + pub fn find_short_term_with_pic_num(&self, pic_num: i32) -> Option<&DpbEntry> { + let position = self.find_short_term_with_pic_num_pos(pic_num)?; + Some(&self.entries[position]) + } + + /// Find a long term reference picture with the given `long_term_pic_num` + /// value. + fn find_long_term_with_long_term_pic_num_pos(&self, long_term_pic_num: u32) -> Option { + let position = self.pictures().position(|p| { + matches!(p.reference(), Reference::LongTerm) && p.long_term_pic_num == long_term_pic_num + }); + + log::debug!( + "find_long_term_with_long_term_pic_num: {}, found position {:?}", + long_term_pic_num, + position + ); + + position + } + + /// Find a long term reference picture with the given `long_term_pic_num` + /// value. + pub fn find_long_term_with_long_term_pic_num( + &self, + long_term_pic_num: u32, + ) -> Option<&DpbEntry> { + let position = self.find_long_term_with_long_term_pic_num_pos(long_term_pic_num)?; + Some(&self.entries[position]) + } + + /// Store `picture` and its backend handle in the DPB. + pub fn store_picture( + &mut self, + picture: RcPictureData, + handle: Option, + ) -> Result<(), StorePictureError> { + let max_pics = if self.interlaced { self.max_num_pics * 2 } else { self.max_num_pics }; + + if self.entries.len() >= max_pics { + return Err(StorePictureError::DpbIsFull); + } + + let pic = picture.borrow(); + + // C.4.2. Decoding of gaps in frame_num and storage of "non-existing" + // pictures + let needed_for_output = !pic.nonexisting; + + debug!( + "Stored picture POC {:?}, field {:?}, the DPB length is {:?}", + pic.pic_order_cnt, + pic.field, + self.entries.len() + ); + drop(pic); + + self.entries.push(DpbEntry { + pic: picture, + reference: handle.clone(), + decoded_frame: handle, + needed_for_output, + }); + + Ok(()) + } + + /// Whether the DPB has an empty slot for a new picture. + pub fn has_empty_frame_buffer(&self) -> bool { + let count = if !self.interlaced { + self.entries.len() + } else { + self.pictures() + .filter(|pic| { + matches!(pic.field_rank(), FieldRank::First(..)) + || (matches!(pic.field_rank(), FieldRank::Single) + && pic.field == Field::Frame) + }) + .count() + }; + + count < self.max_num_pics + } + + /// Whether the DPB needs bumping, as described by clauses 1, 4, 5, 6 of + /// C.4.5.3 "Bumping" process. + pub fn needs_bumping(&self, to_insert: &PictureData) -> bool { + // In C.4.5.3 we handle clauses 2 and 3 separately. All other clauses + // check for an empty frame buffer first. Here we handle: + // - There is no empty frame buffer and a empty frame buffer is + // needed for storage of an inferred "non-existing" frame. + // + // - There is no empty frame buffer and an empty frame buffer is + // needed for storage of a decoded (non-IDR) reference picture. + // + // - There is no empty frame buffer and the current picture is a non- + // reference picture that is not the second field of a complementary + // non-reference field pair and there are pictures in the DPB that + // are marked as "needed for output" that precede the current + // non-reference picture in output order. + // + // Clauses 2 and 3 are handled by H264Codec::handle_picture and + // H264Codec::finish_picture, respectively. + if self.has_empty_frame_buffer() { + return false; + } + + if to_insert.nonexisting { + return true; + } + + let non_idr_ref = to_insert.is_ref() && matches!(to_insert.is_idr, IsIdr::No); + if non_idr_ref { + return true; + } + + let lowest_poc = match self.find_lowest_poc_for_bumping() { + Some(handle) => handle.pic.borrow().pic_order_cnt, + None => return false, + }; + + !to_insert.is_second_field_of_complementary_ref_pair() + && to_insert.pic_order_cnt > lowest_poc + } + + /// Find the lowest POC in the DPB that can be bumped. + fn find_lowest_poc_for_bumping(&self) -> Option<&DpbEntry> { + self.entries + .iter() + .filter(|entry| entry.is_bumpable()) + .min_by_key(|handle| handle.pic.borrow().pic_order_cnt) + } + + /// Find the lowest POC in the DPB that can be bumped and return a mutable reference. + fn find_lowest_poc_for_bumping_mut(&mut self) -> Option<&mut DpbEntry> { + self.entries + .iter_mut() + .filter(|entry| entry.is_bumpable()) + .min_by_key(|handle| handle.pic.borrow().pic_order_cnt) + } + + /// Bump the dpb, returning a picture as per the bumping process described in C.4.5.3. + /// Note that this picture will still be referenced by its pair, if any. + fn bump(&mut self) -> Option> { + let dpb_entry = self.find_lowest_poc_for_bumping_mut()?; + let handle = dpb_entry.decoded_frame.take(); + let pic = dpb_entry.pic.borrow(); + + debug!("Bumping picture {:#?} from the dpb", pic); + + dpb_entry.needed_for_output = false; + // Lookup the second field entry and flip as well. + // `find_lowest_poc_for_bumping_mut` always returns the first field, never the second. + if let FieldRank::First(second_field) = pic.field_rank() { + let second_field = second_field.upgrade(); + drop(pic); + if let Some(second_field) = + second_field.and_then(|f| self.entries.iter_mut().find(|e| Rc::ptr_eq(&f, &e.pic))) + { + second_field.needed_for_output = false; + } + } + + Some(handle) + } + + /// Drains the DPB by continuously invoking the bumping process. + pub fn drain(&mut self) -> Vec> { + debug!("Draining the DPB."); + + let mut pics = vec![]; + + while let Some(pic) = self.bump() { + pics.push(pic); + } + + self.clear(); + + pics + } + + /// Clears the DPB, dropping all the pictures. + pub fn clear(&mut self) { + debug!("Clearing the DPB"); + + let max_num_pics = self.max_num_pics; + let interlaced = self.interlaced; + + *self = Default::default(); + + self.max_num_pics = max_num_pics; + self.interlaced = interlaced; + } + + /// Returns an iterator of short term refs. + pub fn short_term_refs_iter(&self) -> impl Iterator> { + self.entries + .iter() + .filter(|&handle| matches!(handle.pic.borrow().reference(), Reference::ShortTerm)) + } + + /// Returns an iterator of long term refs. + pub fn long_term_refs_iter(&self) -> impl Iterator> { + self.entries + .iter() + .filter(|&handle| matches!(handle.pic.borrow().reference(), Reference::LongTerm)) + } + + pub fn update_pic_nums( + &mut self, + frame_num: u32, + max_frame_num: u32, + current_pic: &PictureData, + ) { + for mut pic in self.pictures_mut() { + if !pic.is_ref() { + continue; + } + + if *pic.reference() == Reference::LongTerm { + pic.long_term_pic_num = if current_pic.field == Field::Frame { + pic.long_term_frame_idx + } else if current_pic.field == pic.field { + 2 * pic.long_term_frame_idx + 1 + } else { + 2 * pic.long_term_frame_idx + }; + } else { + pic.frame_num_wrap = if pic.frame_num > frame_num { + pic.frame_num as i32 - max_frame_num as i32 + } else { + pic.frame_num as i32 + }; + + pic.pic_num = if current_pic.field == Field::Frame { + pic.frame_num_wrap + } else if pic.field == current_pic.field { + 2 * pic.frame_num_wrap + 1 + } else { + 2 * pic.frame_num_wrap + }; + } + } + } + + /// Bumps the DPB if needed. DPB bumping is described on C.4.5.3. + pub fn bump_as_needed(&mut self, current_pic: &PictureData) -> Vec> { + let mut pics = vec![]; + while self.needs_bumping(current_pic) && self.len() >= self.max_num_reorder_frames { + match self.bump() { + Some(pic) => pics.push(pic), + None => return pics, + } + self.remove_unused(); + } + + pics + } + + // 8.2.5.3 + pub fn sliding_window_marking(&mut self, pic: &mut PictureData, sps: &Sps) { + // If the current picture is a coded field that is the second field in + // decoding order of a complementary reference field pair, and the first + // field has been marked as "used for short-term reference", the current + // picture and the complementary reference field pair are also marked as + // "used for short-term reference". + if let FieldRank::Second(other_field) = pic.field_rank() { + if matches!(other_field.borrow().reference(), Reference::ShortTerm) { + pic.set_reference(Reference::ShortTerm, false); + return; + } + } + + let mut num_ref_pics = self.num_ref_frames(); + let max_num_ref_frames = std::cmp::max(1, sps.max_num_ref_frames as usize); + + if num_ref_pics < max_num_ref_frames { + return; + } + + while num_ref_pics >= max_num_ref_frames { + if let Some(to_unmark) = self.find_short_term_lowest_frame_num_wrap() { + to_unmark.pic.borrow_mut().set_reference(Reference::None, true); + num_ref_pics -= 1; + } else { + log::warn!("could not find a ShortTerm picture to unmark in the DPB"); + break; + } + } + + self.remove_unused(); + } + + pub fn mmco_op_1( + &mut self, + pic: &PictureData, + marking: &RefPicMarkingInner, + ) -> Result<(), MmcoError> { + let pic_num_x = pic.pic_num - (marking.difference_of_pic_nums_minus1 as i32 + 1); + + log::debug!("MMCO op 1 for pic_num_x {}", pic_num_x); + log::trace!("Dpb state before MMCO=1: {:#?}", self); + + let to_mark = + self.find_short_term_with_pic_num(pic_num_x).ok_or(MmcoError::NoShortTermPic)?; + + to_mark.pic.borrow_mut().set_reference(Reference::None, matches!(pic.field, Field::Frame)); + + Ok(()) + } + + pub fn mmco_op_2( + &mut self, + pic: &PictureData, + marking: &RefPicMarkingInner, + ) -> Result<(), MmcoError> { + log::debug!("MMCO op 2 for long_term_pic_num {}", marking.long_term_pic_num); + + log::trace!("Dpb state before MMCO=2: {:#?}", self); + + let to_mark = self + .find_long_term_with_long_term_pic_num(marking.long_term_pic_num) + .ok_or(MmcoError::NoShortTermPic)?; + + to_mark.pic.borrow_mut().set_reference(Reference::None, matches!(pic.field, Field::Frame)); + + Ok(()) + } + + pub fn mmco_op_3( + &mut self, + pic: &PictureData, + marking: &RefPicMarkingInner, + ) -> Result<(), MmcoError> { + let pic_num_x = pic.pic_num - (marking.difference_of_pic_nums_minus1 as i32 + 1); + + log::debug!("MMCO op 3 for pic_num_x {}", pic_num_x); + log::trace!("Dpb state before MMCO=3: {:#?}", self); + + let to_mark_as_long_pos = + self.find_short_term_with_pic_num_pos(pic_num_x).ok_or(MmcoError::NoShortTermPic)?; + let to_mark_as_long = &self.entries[to_mark_as_long_pos].pic; + + if !matches!(to_mark_as_long.borrow().reference(), Reference::ShortTerm) { + return Err(MmcoError::ExpectedMarked); + } + + if to_mark_as_long.borrow().nonexisting { + return Err(MmcoError::ExpectedExisting); + } + + let to_mark_as_long_ptr = to_mark_as_long.as_ptr(); + let to_mark_as_long_other_field_ptr = + to_mark_as_long.borrow().other_field().map(|f| f.as_ptr()); + + let long_term_frame_idx = marking.long_term_frame_idx; + + for mut picture in self.pictures_mut() { + let long_already_assigned = matches!(picture.reference(), Reference::LongTerm) + && picture.long_term_frame_idx == long_term_frame_idx; + + if long_already_assigned { + let is_frame = matches!(picture.field, Field::Frame); + + let is_complementary_field_pair = picture + .other_field() + .map(|f| { + let pic = f.borrow(); + matches!(pic.reference(), Reference::LongTerm) + && pic.long_term_frame_idx == long_term_frame_idx + }) + .unwrap_or(false); + + // When LongTermFrameIdx equal to + // long_term_frame_idx is already assigned to a + // long-term reference frame or a long-term + // complementary reference field pair, that frame or + // complementary field pair and both of its fields + // are marked as "unused for reference" + if is_frame || is_complementary_field_pair { + picture.set_reference(Reference::None, true); + break; + } + + // When LongTermFrameIdx is already assigned to a + // reference field, and that reference field is not + // part of a complementary field pair that includes + // the picture specified by picNumX, that field is + // marked as "unused for reference". + let reference_field_is_not_part_of_pic_x = match picture.other_field() { + None => true, + Some(other_field) => { + // Check that the fields do not reference one another. + !std::ptr::eq(other_field.as_ptr(), to_mark_as_long_ptr) + && to_mark_as_long_other_field_ptr + .map(|p| !std::ptr::eq(p, &(*picture))) + .unwrap_or(true) + } + }; + + if reference_field_is_not_part_of_pic_x { + picture.set_reference(Reference::None, false); + break; + } + } + } + + let is_frame = matches!(pic.field, Field::Frame); + let to_mark_as_long = &self.entries[to_mark_as_long_pos].pic; + to_mark_as_long.borrow_mut().set_reference(Reference::LongTerm, is_frame); + to_mark_as_long.borrow_mut().long_term_frame_idx = long_term_frame_idx; + + if let Some(other_field) = to_mark_as_long.borrow().other_field() { + let mut other_field = other_field.borrow_mut(); + if matches!(other_field.reference(), Reference::LongTerm) { + other_field.long_term_frame_idx = long_term_frame_idx; + + log::debug!( + "Assigned long_term_frame_idx {} to other_field {:#?}", + long_term_frame_idx, + &other_field + ); + } + } + + Ok(()) + } + + /// Returns the new `max_long_term_frame_idx`. + pub fn mmco_op_4(&mut self, marking: &RefPicMarkingInner) -> MaxLongTermFrameIdx { + log::debug!("MMCO op 4, max_long_term_frame_idx: {:?}", marking.max_long_term_frame_idx); + + log::trace!("Dpb state before MMCO=4: {:#?}", self); + + for mut dpb_pic in self + .pictures_mut() + .filter(|pic| matches!(pic.reference(), Reference::LongTerm)) + .filter(|pic| marking.max_long_term_frame_idx < pic.long_term_frame_idx) + { + dpb_pic.set_reference(Reference::None, false); + } + + marking.max_long_term_frame_idx + } + + /// Returns the new `max_long_term_frame_idx`. + pub fn mmco_op_5(&mut self, pic: &mut PictureData) -> MaxLongTermFrameIdx { + log::debug!("MMCO op 5, marking all pictures in the DPB as unused for reference"); + log::trace!("Dpb state before MMCO=5: {:#?}", self); + + self.mark_all_as_unused_for_ref(); + + pic.has_mmco_5 = true; + + // A picture including a memory_management_control_operation equal to 5 + // shall have frame_num constraints as described above and, after the + // decoding of the current picture and the processing of the memory + // management control operations, the picture shall be inferred to have + // had frame_num equal to 0 for all subsequent use in the decoding + // process, except as specified in clause 7.4.1.2.4. + pic.frame_num = 0; + + // When the current picture includes a + // memory_management_control_operation equal to 5, after the decoding of + // the current picture, tempPicOrderCnt is set equal to PicOrderCnt( + // CurrPic ), TopFieldOrderCnt of the current picture (if any) is set + // equal to TopFieldOrderCnt − tempPicOrderCnt, and BottomFieldOrderCnt + // of the current picture (if any) is set equal to BottomFieldOrderCnt − + // tempPicOrderCnt + match pic.field { + Field::Top => { + pic.top_field_order_cnt = 0; + pic.pic_order_cnt = 0; + } + Field::Bottom => { + pic.bottom_field_order_cnt = 0; + pic.pic_order_cnt = 0; + } + Field::Frame => { + pic.top_field_order_cnt -= pic.pic_order_cnt; + pic.bottom_field_order_cnt -= pic.pic_order_cnt; + pic.pic_order_cnt = + std::cmp::min(pic.top_field_order_cnt, pic.bottom_field_order_cnt); + } + } + + MaxLongTermFrameIdx::NoLongTermFrameIndices + } + + pub fn mmco_op_6(&mut self, pic: &mut PictureData, marking: &RefPicMarkingInner) { + let long_term_frame_idx = marking.long_term_frame_idx; + + log::debug!("MMCO op 6, long_term_frame_idx: {}", long_term_frame_idx); + log::trace!("Dpb state before MMCO=6: {:#?}", self); + + for mut dpb_pic in self.pictures_mut() { + // When a variable LongTermFrameIdx equal to long_term_frame_idx is + // already assigned to a long-term reference frame or a long-term + // complementary reference field pair, that frame or complementary + // field pair and both of its fields are marked as "unused for + // reference". When LongTermFrameIdx is already assigned to a + // reference field, and that reference field is not part of a + // complementary field pair that includes the current picture, that + // field is marked as "unused for reference". + if matches!(dpb_pic.reference(), Reference::LongTerm) + && dpb_pic.long_term_frame_idx == long_term_frame_idx + { + let is_frame = matches!(dpb_pic.field, Field::Frame); + + let is_complementary_ref_field_pair = dpb_pic + .other_field() + .map(|f| { + let pic = f.borrow(); + matches!(pic.reference(), Reference::LongTerm) + && pic.long_term_frame_idx == long_term_frame_idx + }) + .unwrap_or(false); + + dpb_pic.set_reference(Reference::None, is_frame || is_complementary_ref_field_pair); + + break; + } + } + + let is_frame = matches!(pic.field, Field::Frame); + + let is_second_ref_field = match pic.field_rank() { + FieldRank::Second(first_field) + if *first_field.borrow().reference() == Reference::LongTerm => + { + first_field.borrow_mut().long_term_frame_idx = long_term_frame_idx; + true + } + _ => false, + }; + + pic.set_reference(Reference::LongTerm, is_frame || is_second_ref_field); + pic.long_term_frame_idx = long_term_frame_idx; + } + + #[cfg(debug_assertions)] + fn debug_ref_list_p(ref_pic_list: &[&DpbEntry], field_pic: bool) { + debug!( + "ref_list_p0: (ShortTerm|LongTerm, pic_num) {:?}", + ref_pic_list + .iter() + .map(|h| { + let p = h.pic.borrow(); + let reference = match p.reference() { + Reference::None => panic!("Not a reference."), + Reference::ShortTerm => "ShortTerm", + Reference::LongTerm => "LongTerm", + }; + + let field = if !p.is_second_field() { "First field" } else { "Second field" }; + + let field = format!("{}, {:?}", field, p.field); + + let inner = match (field_pic, p.reference()) { + (false, _) => ("pic_num", p.pic_num, field), + (true, Reference::ShortTerm) => ("frame_num_wrap", p.frame_num_wrap, field), + (true, Reference::LongTerm) => { + ("long_term_frame_idx", p.long_term_frame_idx as i32, field) + } + + _ => panic!("Not a reference."), + }; + (reference, inner) + }) + .collect::>() + ); + } + + #[cfg(debug_assertions)] + fn debug_ref_list_b(ref_pic_list: &[&DpbEntry], ref_pic_list_name: &str) { + debug!( + "{:?}: (ShortTerm|LongTerm, (POC|LongTermPicNum)) {:?}", + ref_pic_list_name, + ref_pic_list + .iter() + .map(|h| { + let p = h.pic.borrow(); + let reference = match p.reference() { + Reference::None => panic!("Not a reference."), + Reference::ShortTerm => "ShortTerm", + Reference::LongTerm => "LongTerm", + }; + + let field = if !p.is_second_field() { "First field" } else { "Second field" }; + + let field = format!("{}, {:?}", field, p.field); + + let inner = match p.reference() { + Reference::ShortTerm => ("POC", p.pic_order_cnt, field), + Reference::LongTerm => { + ("LongTermPicNum", p.long_term_pic_num as i32, field) + } + _ => panic!("Not a reference!"), + }; + (reference, inner) + }) + .collect::>() + ); + } + + fn sort_pic_num_descending(pics: &mut [&DpbEntry]) { + pics.sort_by_key(|h| std::cmp::Reverse(h.pic.borrow().pic_num)); + } + + fn sort_frame_num_wrap_descending(pics: &mut [&DpbEntry]) { + pics.sort_by_key(|h| std::cmp::Reverse(h.pic.borrow().frame_num_wrap)); + } + + fn sort_long_term_pic_num_ascending(pics: &mut [&DpbEntry]) { + pics.sort_by_key(|h| h.pic.borrow().long_term_pic_num); + } + + fn sort_long_term_frame_idx_ascending(pics: &mut [&DpbEntry]) { + pics.sort_by_key(|h| h.pic.borrow().long_term_frame_idx); + } + + fn sort_poc_descending(pics: &mut [&DpbEntry]) { + pics.sort_by_key(|h| std::cmp::Reverse(h.pic.borrow().pic_order_cnt)); + } + + fn sort_poc_ascending(pics: &mut [&DpbEntry]) { + pics.sort_by_key(|h| h.pic.borrow().pic_order_cnt); + } + + // When the reference picture list RefPicList1 has more than one entry + // and RefPicList1 is identical to the reference picture list + // RefPicList0, the first two entries RefPicList1[0] and RefPicList1[1] + // are switched. + fn swap_b1_if_needed(b0: &DpbPicRefList, b1: &mut DpbPicRefList) { + if b1.len() > 1 && b0.len() == b1.len() { + let mut equals = true; + for (x1, x2) in b0.iter().zip(b1.iter()) { + if !Rc::ptr_eq(&x1.pic, &x2.pic) { + equals = false; + break; + } + } + + if equals { + b1.swap(0, 1); + } + } + } + + /// Copies from refFrameList(XShort|Long)Term into RefPicListX as per 8.2.4.2.5. Used when + /// building the reference list for fields in interlaced decoding. + fn init_ref_field_pic_list<'a>( + mut field: Field, + reference_type: Reference, + ref_frame_list: &mut DpbPicRefList<'a, T>, + ref_pic_list: &mut DpbPicRefList<'a, T>, + ) { + // When one field of a reference frame was not decoded or is not marked as "used for + // (short|long)-term reference", the missing field is ignored and instead the next + // available stored reference field of the chosen parity from the ordered list of frames + // refFrameListX(Short|Long)Term is inserted into RefPicListX. + ref_frame_list.retain(|h| { + let p = h.pic.borrow(); + let skip = p.nonexisting || *p.reference() != reference_type; + !skip + }); + + while let Some(position) = ref_frame_list.iter().position(|h| { + let p = h.pic.borrow(); + let found = p.field == field; + + if found { + field = field.opposite(); + } + + found + }) { + let pic = ref_frame_list.remove(position); + ref_pic_list.push(pic); + } + + ref_pic_list.append(ref_frame_list); + } + + /// 8.2.4.2.1 Initialization process for the reference picture list for P + /// and SP slices in frames + fn build_ref_pic_list_p(&self) -> DpbPicRefList { + let mut ref_pic_list_p0: Vec<_> = + self.short_term_refs_iter().filter(|h| !h.pic.borrow().is_second_field()).collect(); + + Self::sort_pic_num_descending(&mut ref_pic_list_p0); + + let num_short_term_refs = ref_pic_list_p0.len(); + + ref_pic_list_p0 + .extend(self.long_term_refs_iter().filter(|h| !h.pic.borrow().is_second_field())); + Self::sort_long_term_pic_num_ascending(&mut ref_pic_list_p0[num_short_term_refs..]); + + #[cfg(debug_assertions)] + Self::debug_ref_list_p(&ref_pic_list_p0, false); + + ref_pic_list_p0 + } + + /// 8.2.4.2.2 Initialization process for the reference picture list for P + /// and SP slices in fields + fn build_ref_field_pic_list_p(&self, cur_pic: &PictureData) -> DpbPicRefList { + let mut ref_pic_list_p0 = vec![]; + + let mut ref_frame_list_0_short_term: Vec<_> = self.short_term_refs_iter().collect(); + Self::sort_frame_num_wrap_descending(&mut ref_frame_list_0_short_term); + + let mut ref_frame_list_long_term: Vec<_> = self.long_term_refs_iter().collect(); + Self::sort_long_term_pic_num_ascending(&mut ref_frame_list_long_term); + + // 8.2.4.2.5 + Self::init_ref_field_pic_list( + cur_pic.field, + Reference::ShortTerm, + &mut ref_frame_list_0_short_term, + &mut ref_pic_list_p0, + ); + Self::init_ref_field_pic_list( + cur_pic.field, + Reference::LongTerm, + &mut ref_frame_list_long_term, + &mut ref_pic_list_p0, + ); + + #[cfg(debug_assertions)] + Self::debug_ref_list_p(&ref_pic_list_p0, true); + + ref_pic_list_p0 + } + + // 8.2.4.2.3 Initialization process for reference picture lists for B slices + // in frames + fn build_ref_pic_list_b(&self, cur_pic: &PictureData) -> (DpbPicRefList, DpbPicRefList) { + let mut short_term_refs: Vec<_> = + self.short_term_refs_iter().filter(|h| !h.pic.borrow().is_second_field()).collect(); + + // When pic_order_cnt_type is equal to 0, reference pictures that are + // marked as "non-existing" as specified in clause 8.2.5.2 are not + // included in either RefPicList0 or RefPicList1. + if cur_pic.pic_order_cnt_type == 0 { + short_term_refs.retain(|h| !h.pic.borrow().nonexisting); + } + + let mut ref_pic_list_b0 = vec![]; + let mut ref_pic_list_b1 = vec![]; + let mut remaining = vec![]; + // b0 contains three inner lists of pictures, i.e. [[0] [1] [2]] + // [0]: short term pictures with POC < current, sorted by descending POC. + // [1]: short term pictures with POC > current, sorted by ascending POC. + // [2]: long term pictures sorted by ascending long_term_pic_num + for &handle in &short_term_refs { + let pic = handle.pic.borrow(); + + if pic.pic_order_cnt < cur_pic.pic_order_cnt { + ref_pic_list_b0.push(handle); + } else { + remaining.push(handle); + } + } + + Self::sort_poc_descending(&mut ref_pic_list_b0); + Self::sort_poc_ascending(&mut remaining); + ref_pic_list_b0.append(&mut remaining); + + let mut long_term_refs: Vec<_> = self + .long_term_refs_iter() + .filter(|h| !h.pic.borrow().nonexisting) + .filter(|h| !h.pic.borrow().is_second_field()) + .collect(); + Self::sort_long_term_pic_num_ascending(&mut long_term_refs); + + ref_pic_list_b0.extend(long_term_refs.clone()); + + // b1 contains three inner lists of pictures, i.e. [[0] [1] [2]] + // [0]: short term pictures with POC > current, sorted by ascending POC. + // [1]: short term pictures with POC < current, sorted by descending POC. + // [2]: long term pictures sorted by ascending long_term_pic_num + for &handle in &short_term_refs { + let pic = handle.pic.borrow(); + + if pic.pic_order_cnt > cur_pic.pic_order_cnt { + ref_pic_list_b1.push(handle); + } else { + remaining.push(handle); + } + } + + Self::sort_poc_ascending(&mut ref_pic_list_b1); + Self::sort_poc_descending(&mut remaining); + + ref_pic_list_b1.extend(remaining); + ref_pic_list_b1.extend(long_term_refs); + + // When the reference picture list RefPicList1 has more than one entry + // and RefPicList1 is identical to the reference picture list + // RefPicList0, the first two entries RefPicList1[0] and RefPicList1[1] + // are switched. + Self::swap_b1_if_needed(&ref_pic_list_b0, &mut ref_pic_list_b1); + + #[cfg(debug_assertions)] + Self::debug_ref_list_b(&ref_pic_list_b0, "ref_pic_list_b0"); + #[cfg(debug_assertions)] + Self::debug_ref_list_b(&ref_pic_list_b1, "ref_pic_list_b1"); + + (ref_pic_list_b0, ref_pic_list_b1) + } + + /// 8.2.4.2.4 Initialization process for reference picture lists for B + /// slices in fields + fn build_ref_field_pic_list_b( + &self, + cur_pic: &PictureData, + ) -> (DpbPicRefList, DpbPicRefList) { + let mut ref_pic_list_b0 = vec![]; + let mut ref_pic_list_b1 = vec![]; + let mut ref_frame_list_0_short_term = vec![]; + let mut ref_frame_list_1_short_term = vec![]; + + let mut remaining = vec![]; + + let mut short_term_refs: Vec<_> = self.short_term_refs_iter().collect(); + + // When pic_order_cnt_type is equal to 0, reference pictures that are + // marked as "non-existing" as specified in clause 8.2.5.2 are not + // included in either RefPicList0 or RefPicList1. + if cur_pic.pic_order_cnt_type == 0 { + short_term_refs.retain(|h| !h.pic.borrow().nonexisting); + } + + // refFrameList0ShortTerm is comprised of two inner lists, [[0] [1]] + // [0]: short term pictures with POC <= current, sorted by descending POC + // [1]: short term pictures with POC > current, sorted by ascending POC + // NOTE 3 – When the current field follows in decoding order a coded + // field fldPrev with which together it forms a complementary reference + // field pair, fldPrev is included into the list refFrameList0ShortTerm + // using PicOrderCnt( fldPrev ) and the ordering method described in the + // previous sentence is applied. + for &handle in &short_term_refs { + let pic = handle.pic.borrow(); + + if pic.pic_order_cnt <= cur_pic.pic_order_cnt { + ref_frame_list_0_short_term.push(handle); + } else { + remaining.push(handle); + } + } + + Self::sort_poc_descending(&mut ref_frame_list_0_short_term); + Self::sort_poc_ascending(&mut remaining); + ref_frame_list_0_short_term.append(&mut remaining); + + // refFrameList1ShortTerm is comprised of two inner lists, [[0] [1]] + // [0]: short term pictures with POC > current, sorted by ascending POC + // [1]: short term pictures with POC <= current, sorted by descending POC + // NOTE 4 – When the current field follows in decoding order a coded + // field fldPrev with which together it forms a complementary reference + // field pair, fldPrev is included into the list refFrameList1ShortTerm + // using PicOrderCnt( fldPrev ) and the ordering method described in the + // previous sentence is applied. + + for &handle in &short_term_refs { + let pic = handle.pic.borrow(); + + if pic.pic_order_cnt > cur_pic.pic_order_cnt { + ref_frame_list_1_short_term.push(handle); + } else { + remaining.push(handle); + } + } + + Self::sort_poc_ascending(&mut ref_frame_list_1_short_term); + Self::sort_poc_descending(&mut remaining); + ref_frame_list_1_short_term.append(&mut remaining); + + // refFrameListLongTerm: long term pictures sorted by ascending + // LongTermFrameIdx. + // NOTE 5 – When the current picture is the second field of a + // complementary field pair and the first field of the complementary + // field pair is marked as "used for long-term reference", the first + // field is included into the list refFrameListLongTerm. A reference + // entry in which only one field is marked as "used for long-term + // reference" is included into the list refFrameListLongTerm + let mut ref_frame_list_long_term: Vec<_> = + self.long_term_refs_iter().filter(|h| !h.pic.borrow().nonexisting).collect(); + + Self::sort_long_term_frame_idx_ascending(&mut ref_frame_list_long_term); + + #[cfg(debug_assertions)] + Self::debug_ref_list_b(&ref_frame_list_0_short_term, "ref_frame_list_0_short_term"); + #[cfg(debug_assertions)] + Self::debug_ref_list_b(&ref_frame_list_1_short_term, "ref_frame_list_1_short_term"); + #[cfg(debug_assertions)] + Self::debug_ref_list_b(&ref_frame_list_long_term, "ref_frame_list_long_term"); + + // 8.2.4.2.5 + let field = cur_pic.field; + Self::init_ref_field_pic_list( + field, + Reference::ShortTerm, + &mut ref_frame_list_0_short_term, + &mut ref_pic_list_b0, + ); + Self::init_ref_field_pic_list( + field, + Reference::LongTerm, + &mut ref_frame_list_long_term, + &mut ref_pic_list_b0, + ); + + Self::init_ref_field_pic_list( + field, + Reference::ShortTerm, + &mut ref_frame_list_1_short_term, + &mut ref_pic_list_b1, + ); + Self::init_ref_field_pic_list( + field, + Reference::LongTerm, + &mut ref_frame_list_long_term, + &mut ref_pic_list_b1, + ); + + // When the reference picture list RefPicList1 has more than one entry + // and RefPicList1 is identical to the reference picture list + // RefPicList0, the first two entries RefPicList1[0] and RefPicList1[1] + // are switched. + Self::swap_b1_if_needed(&ref_pic_list_b0, &mut ref_pic_list_b1); + + #[cfg(debug_assertions)] + Self::debug_ref_list_b(&ref_pic_list_b0, "ref_pic_list_b0"); + #[cfg(debug_assertions)] + Self::debug_ref_list_b(&ref_pic_list_b1, "ref_pic_list_b1"); + + (ref_pic_list_b0, ref_pic_list_b1) + } + + /// Returns the lists of reference pictures for `pic`. + pub fn build_ref_pic_lists(&self, pic: &PictureData) -> ReferencePicLists { + let num_refs = self.pictures().filter(|p| p.is_ref() && !p.nonexisting).count(); + + // 8.2.4.2.1 ~ 8.2.4.2.4: When this process is invoked, there shall be + // at least one reference frame or complementary reference field pair + // that is currently marked as "used for reference" (i.e., as "used for + // short-term reference" or "used for long-term reference") and is not + // marked as "non-existing". + if num_refs == 0 { + return Default::default(); + } + + let (ref_pic_list_p0, (ref_pic_list_b0, ref_pic_list_b1)) = + if matches!(pic.field, Field::Frame) { + (self.build_ref_pic_list_p(), self.build_ref_pic_list_b(pic)) + } else { + (self.build_ref_field_pic_list_p(pic), self.build_ref_field_pic_list_b(pic)) + }; + + let dpb_start = self.entries.as_ptr(); + let refs_to_index = |refs: Vec<_>| { + refs.into_iter() + .map(|r| r as *const DpbEntry) + .map(|r| unsafe { r.offset_from(dpb_start) }) + .map(|i| i as usize) + .collect() + }; + + ReferencePicLists { + ref_pic_list_p0: refs_to_index(ref_pic_list_p0), + ref_pic_list_b0: refs_to_index(ref_pic_list_b0), + ref_pic_list_b1: refs_to_index(ref_pic_list_b1), + } + } +} + +impl Default for Dpb { + fn default() -> Self { + // See https://github.com/rust-lang/rust/issues/26925 on why this can't + // be derived. + Self { + entries: Default::default(), + max_num_pics: Default::default(), + max_num_reorder_frames: Default::default(), + interlaced: Default::default(), + } + } +} + +impl std::fmt::Debug for Dpb { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let pics = self.entries.iter().map(|h| &h.pic).enumerate().collect::>(); + f.debug_struct("Dpb") + .field("pictures", &pics) + .field("max_num_pics", &self.max_num_pics) + .field("interlaced", &self.interlaced) + .finish() + } +} diff --git a/vendor/cros-codecs/src/codec/h264/nalu.rs b/vendor/cros-codecs/src/codec/h264/nalu.rs new file mode 100644 index 00000000..82e5b489 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/nalu.rs @@ -0,0 +1,118 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::borrow::Cow; +use std::fmt::Debug; +use std::io::Cursor; +use std::io::Seek; +use std::io::SeekFrom; + +#[allow(clippy::len_without_is_empty)] +pub trait Header: Sized { + /// Parse the NALU header, returning it. + fn parse>(cursor: &mut Cursor) -> Result; + /// Whether this header type indicates EOS. + fn is_end(&self) -> bool; + /// The length of the header. + fn len(&self) -> usize; +} + +#[derive(Debug)] +pub struct Nalu<'a, U> { + pub header: U, + /// The mapping that backs this NALU. Possibly shared with the other NALUs + /// in the Access Unit. + pub data: Cow<'a, [u8]>, + + pub size: usize, + pub offset: usize, +} + +impl<'a, U> Nalu<'a, U> +where + U: Debug + Header, +{ + /// Find the next Annex B encoded NAL unit. + pub fn next(cursor: &mut Cursor<&'a [u8]>) -> Result, String> { + let bitstream = cursor.clone().into_inner(); + let pos = usize::try_from(cursor.position()).map_err(|err| err.to_string())?; + + // Find the start code for this NALU + let current_nalu_offset = match Nalu::<'a, U>::find_start_code(cursor, pos) { + Some(offset) => offset, + None => return Err("No NAL found".into()), + }; + + let mut start_code_offset = pos + current_nalu_offset; + + // If the preceding byte is 00, then we actually have a four byte SC, + // i.e. 00 00 00 01 Where the first 00 is the "zero_byte()" + if start_code_offset > 0 && cursor.get_ref()[start_code_offset - 1] == 00 { + start_code_offset -= 1; + } + + // The NALU offset is its offset + 3 bytes to skip the start code. + let nalu_offset = pos + current_nalu_offset + 3; + + // Set the bitstream position to the start of the current NALU + cursor.set_position(u64::try_from(nalu_offset).map_err(|err| err.to_string())?); + + let hdr = U::parse(cursor)?; + + // Find the start of the subsequent NALU. + let mut next_nalu_offset = match Nalu::<'a, U>::find_start_code(cursor, nalu_offset) { + Some(offset) => offset, + None => { + let cur_pos = cursor.position(); + let end_pos = cursor.seek(SeekFrom::End(0)).map_err(|err| err.to_string())?; + let _ = cursor.seek(SeekFrom::Start(cur_pos)).map_err(|err| err.to_string())?; + (end_pos - cur_pos) as usize + } // Whatever data is left must be part of the current NALU + }; + + while next_nalu_offset > 0 && cursor.get_ref()[nalu_offset + next_nalu_offset - 1] == 00 { + // Discard trailing_zero_8bits + next_nalu_offset -= 1; + } + + let nal_size = if hdr.is_end() { + // the NALU is comprised of only the header + hdr.len() + } else { + next_nalu_offset + }; + + Ok(Nalu { + header: hdr, + data: Cow::from(&bitstream[start_code_offset..nalu_offset + nal_size]), + size: nal_size, + offset: nalu_offset - start_code_offset, + }) + } +} + +impl<'a, U> Nalu<'a, U> +where + U: Debug, +{ + fn find_start_code(data: &mut Cursor<&'a [u8]>, offset: usize) -> Option { + // discard all zeroes until the start code pattern is found + data.get_ref()[offset..].windows(3).position(|window| window == [0x00, 0x00, 0x01]) + } + + pub fn into_owned(self) -> Nalu<'static, U> { + Nalu { + header: self.header, + size: self.size, + offset: self.offset, + data: Cow::Owned(self.data.into_owned()), + } + } +} + +impl<'a, U> AsRef<[u8]> for Nalu<'a, U> { + fn as_ref(&self) -> &[u8] { + &self.data[self.offset..self.offset + self.size] + } +} diff --git a/vendor/cros-codecs/src/codec/h264/nalu_writer.rs b/vendor/cros-codecs/src/codec/h264/nalu_writer.rs new file mode 100644 index 00000000..90eaec6a --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/nalu_writer.rs @@ -0,0 +1,299 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +use std::fmt; +use std::io::Write; + +use crate::bitstream_utils::BitWriter; +use crate::bitstream_utils::BitWriterError; + +/// Internal wrapper over [`std::io::Write`] for possible emulation prevention +struct EmulationPrevention { + out: W, + prev_bytes: [Option; 2], + + /// Emulation prevention enabled. + ep_enabled: bool, +} + +impl EmulationPrevention { + fn new(writer: W, ep_enabled: bool) -> Self { + Self { out: writer, prev_bytes: [None; 2], ep_enabled } + } + + fn write_byte(&mut self, curr_byte: u8) -> std::io::Result<()> { + if self.prev_bytes[1] == Some(0x00) && self.prev_bytes[0] == Some(0x00) && curr_byte <= 0x03 + { + self.out.write_all(&[0x00, 0x00, 0x03, curr_byte])?; + self.prev_bytes = [None; 2]; + } else { + if let Some(byte) = self.prev_bytes[1] { + self.out.write_all(&[byte])?; + } + + self.prev_bytes[1] = self.prev_bytes[0]; + self.prev_bytes[0] = Some(curr_byte); + } + + Ok(()) + } + + /// Writes a H.264 NALU header. + fn write_header(&mut self, idc: u8, type_: u8) -> NaluWriterResult<()> { + self.out.write_all(&[0x00, 0x00, 0x00, 0x01, (idc & 0b11) << 5 | (type_ & 0b11111)])?; + + Ok(()) + } + + fn has_data_pending(&self) -> bool { + self.prev_bytes[0].is_some() || self.prev_bytes[1].is_some() + } +} + +impl Write for EmulationPrevention { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + if !self.ep_enabled { + self.out.write_all(buf)?; + return Ok(buf.len()); + } + + for byte in buf { + self.write_byte(*byte)?; + } + + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + if let Some(byte) = self.prev_bytes[1].take() { + self.out.write_all(&[byte])?; + } + + if let Some(byte) = self.prev_bytes[0].take() { + self.out.write_all(&[byte])?; + } + + self.out.flush() + } +} + +impl Drop for EmulationPrevention { + fn drop(&mut self) { + if let Err(e) = self.flush() { + log::error!("Unable to flush pending bytes {e:?}"); + } + } +} + +#[derive(Debug)] +pub enum NaluWriterError { + Overflow, + Io(std::io::Error), + BitWriterError(BitWriterError), +} + +impl fmt::Display for NaluWriterError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + NaluWriterError::Overflow => write!(f, "value increment caused value overflow"), + NaluWriterError::Io(x) => write!(f, "{}", x.to_string()), + NaluWriterError::BitWriterError(x) => write!(f, "{}", x.to_string()), + } + } +} + +impl From for NaluWriterError { + fn from(err: std::io::Error) -> Self { + NaluWriterError::Io(err) + } +} + +impl From for NaluWriterError { + fn from(err: BitWriterError) -> Self { + NaluWriterError::BitWriterError(err) + } +} + +pub type NaluWriterResult = std::result::Result; + +/// A writer for H.264 bitstream. It is capable of outputing bitstream with +/// emulation-prevention. +pub struct NaluWriter(BitWriter>); + +impl NaluWriter { + pub fn new(writer: W, ep_enabled: bool) -> Self { + Self(BitWriter::new(EmulationPrevention::new(writer, ep_enabled))) + } + + /// Writes fixed bit size integer (up to 32 bit) output with emulation + /// prevention if enabled. Corresponds to `f(n)` in H.264 spec. + pub fn write_f>(&mut self, bits: usize, value: T) -> NaluWriterResult { + self.0.write_f(bits, value).map_err(NaluWriterError::BitWriterError) + } + + /// An alias to [`Self::write_f`] Corresponds to `n(n)` in H.264 spec. + pub fn write_u>(&mut self, bits: usize, value: T) -> NaluWriterResult { + self.write_f(bits, value) + } + + /// Writes a number in exponential golumb format. + pub fn write_exp_golumb(&mut self, value: u32) -> NaluWriterResult<()> { + let value = value.checked_add(1).ok_or(NaluWriterError::Overflow)?; + let bits = 32 - value.leading_zeros() as usize; + let zeros = bits - 1; + + self.write_f(zeros, 0u32)?; + self.write_f(bits, value)?; + + Ok(()) + } + + /// Writes a unsigned integer in exponential golumb format. + /// Coresponds to `ue(v)` in H.264 spec. + pub fn write_ue>(&mut self, value: T) -> NaluWriterResult<()> { + let value = value.into(); + + self.write_exp_golumb(value) + } + + /// Writes a signed integer in exponential golumb format. + /// Coresponds to `se(v)` in H.264 spec. + pub fn write_se>(&mut self, value: T) -> NaluWriterResult<()> { + let value: i32 = value.into(); + let abs_value: u32 = value.unsigned_abs(); + + if value <= 0 { + self.write_ue(2 * abs_value) + } else { + self.write_ue(2 * abs_value - 1) + } + } + + /// Returns `true` if ['Self`] hold data that wasn't written to [`std::io::Write`] + pub fn has_data_pending(&self) -> bool { + self.0.has_data_pending() || self.0.inner().has_data_pending() + } + + /// Writes a H.264 NALU header. + pub fn write_header(&mut self, idc: u8, _type: u8) -> NaluWriterResult<()> { + self.0.flush()?; + self.0.inner_mut().write_header(idc, _type)?; + Ok(()) + } + + /// Returns `true` if next bits will be aligned to 8 + pub fn aligned(&self) -> bool { + !self.0.has_data_pending() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::bitstream_utils::BitReader; + + #[test] + fn simple_bits() { + let mut buf = Vec::::new(); + { + let mut writer = NaluWriter::new(&mut buf, false); + writer.write_f(1, true).unwrap(); + writer.write_f(1, false).unwrap(); + writer.write_f(1, false).unwrap(); + writer.write_f(1, false).unwrap(); + writer.write_f(1, true).unwrap(); + writer.write_f(1, true).unwrap(); + writer.write_f(1, true).unwrap(); + writer.write_f(1, true).unwrap(); + } + assert_eq!(buf, vec![0b10001111u8]); + } + + #[test] + fn simple_first_few_ue() { + fn single_ue(value: u32) -> Vec { + let mut buf = Vec::::new(); + { + let mut writer = NaluWriter::new(&mut buf, false); + writer.write_ue(value).unwrap(); + } + buf + } + + assert_eq!(single_ue(0), vec![0b10000000u8]); + assert_eq!(single_ue(1), vec![0b01000000u8]); + assert_eq!(single_ue(2), vec![0b01100000u8]); + assert_eq!(single_ue(3), vec![0b00100000u8]); + assert_eq!(single_ue(4), vec![0b00101000u8]); + assert_eq!(single_ue(5), vec![0b00110000u8]); + assert_eq!(single_ue(6), vec![0b00111000u8]); + assert_eq!(single_ue(7), vec![0b00010000u8]); + assert_eq!(single_ue(8), vec![0b00010010u8]); + assert_eq!(single_ue(9), vec![0b00010100u8]); + } + + #[test] + fn writer_reader() { + let mut buf = Vec::::new(); + { + let mut writer = NaluWriter::new(&mut buf, false); + writer.write_ue(10u32).unwrap(); + writer.write_se(-42).unwrap(); + writer.write_se(3).unwrap(); + writer.write_ue(5u32).unwrap(); + } + + let mut reader = BitReader::new(&buf, true); + + assert_eq!(reader.read_ue::().unwrap(), 10); + assert_eq!(reader.read_se::().unwrap(), -42); + assert_eq!(reader.read_se::().unwrap(), 3); + assert_eq!(reader.read_ue::().unwrap(), 5); + + let mut buf = Vec::::new(); + { + let mut writer = NaluWriter::new(&mut buf, false); + writer.write_se(30).unwrap(); + writer.write_ue(100u32).unwrap(); + writer.write_se(-402).unwrap(); + writer.write_ue(50u32).unwrap(); + } + + let mut reader = BitReader::new(&buf, true); + + assert_eq!(reader.read_se::().unwrap(), 30); + assert_eq!(reader.read_ue::().unwrap(), 100); + assert_eq!(reader.read_se::().unwrap(), -402); + assert_eq!(reader.read_ue::().unwrap(), 50); + } + + #[test] + fn writer_emulation_prevention() { + fn test(input: &[u8], bitstream: &[u8]) { + let mut buf = Vec::::new(); + { + let mut writer = NaluWriter::new(&mut buf, true); + for byte in input { + writer.write_f(8, *byte).unwrap(); + } + } + assert_eq!(buf, bitstream); + { + let mut reader = BitReader::new(&buf, true); + for byte in input { + assert_eq!(*byte, reader.read_bits::(8).unwrap()); + } + } + } + + test(&[0x00, 0x00, 0x00], &[0x00, 0x00, 0x03, 0x00]); + test(&[0x00, 0x00, 0x01], &[0x00, 0x00, 0x03, 0x01]); + test(&[0x00, 0x00, 0x02], &[0x00, 0x00, 0x03, 0x02]); + test(&[0x00, 0x00, 0x03], &[0x00, 0x00, 0x03, 0x03]); + + test(&[0x00, 0x00, 0x00, 0x00], &[0x00, 0x00, 0x03, 0x00, 0x00]); + test(&[0x00, 0x00, 0x00, 0x01], &[0x00, 0x00, 0x03, 0x00, 0x01]); + test(&[0x00, 0x00, 0x00, 0x02], &[0x00, 0x00, 0x03, 0x00, 0x02]); + test(&[0x00, 0x00, 0x00, 0x03], &[0x00, 0x00, 0x03, 0x00, 0x03]); + } +} diff --git a/vendor/cros-codecs/src/codec/h264/parser.rs b/vendor/cros-codecs/src/codec/h264/parser.rs new file mode 100644 index 00000000..285242ae --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/parser.rs @@ -0,0 +1,3023 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Can't reasonably expect client code to consume everything that has been parsed. +#![allow(dead_code)] + +use std::collections::BTreeMap; +use std::io::Cursor; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::rc::Rc; + +use crate::bitstream_utils::BitReader; +use crate::codec::h264::nalu; +use crate::codec::h264::nalu::Header; +use crate::codec::h264::picture::Field; + +pub type Nalu<'a> = nalu::Nalu<'a, NaluHeader>; + +pub(super) const DEFAULT_4X4_INTRA: [u8; 16] = + [6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42]; + +pub(super) const DEFAULT_4X4_INTER: [u8; 16] = + [10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34]; + +pub(super) 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, +]; + +pub(super) 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, +]; + +const MAX_PPS_COUNT: u16 = 256; +const MAX_SPS_COUNT: u8 = 32; + +/// The maximum number of pictures in the DPB, as per A.3.1, clause h) +const DPB_MAX_SIZE: usize = 16; + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Point { + pub x: T, + pub y: T, +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Rect { + pub min: Point, + pub max: Point, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum NaluType { + Unknown = 0, + Slice = 1, + SliceDpa = 2, + SliceDpb = 3, + SliceDpc = 4, + SliceIdr = 5, + Sei = 6, + Sps = 7, + Pps = 8, + AuDelimiter = 9, + SeqEnd = 10, + StreamEnd = 11, + FillerData = 12, + SpsExt = 13, + PrefixUnit = 14, + SubsetSps = 15, + DepthSps = 16, + SliceAux = 19, + SliceExt = 20, + SliceDepth = 21, +} + +impl TryFrom for NaluType { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(NaluType::Unknown), + 1 => Ok(NaluType::Slice), + 2 => Ok(NaluType::SliceDpa), + 3 => Ok(NaluType::SliceDpb), + 4 => Ok(NaluType::SliceDpc), + 5 => Ok(NaluType::SliceIdr), + 6 => Ok(NaluType::Sei), + 7 => Ok(NaluType::Sps), + 8 => Ok(NaluType::Pps), + 9 => Ok(NaluType::AuDelimiter), + 10 => Ok(NaluType::SeqEnd), + 11 => Ok(NaluType::StreamEnd), + 12 => Ok(NaluType::FillerData), + 13 => Ok(NaluType::SpsExt), + 14 => Ok(NaluType::PrefixUnit), + 15 => Ok(NaluType::SubsetSps), + 16 => Ok(NaluType::DepthSps), + 19 => Ok(NaluType::SliceAux), + 20 => Ok(NaluType::SliceExt), + 21 => Ok(NaluType::SliceDepth), + _ => Err(format!("Invalid NaluType {}", value)), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct RefPicListModification { + pub modification_of_pic_nums_idc: u8, + /* if modification_of_pic_nums_idc == 0 || 1 */ + pub abs_diff_pic_num_minus1: u32, + /* if modification_of_pic_nums_idc == 2 */ + pub long_term_pic_num: u32, + /* if modification_of_pic_nums_idc == 4 || 5 */ + pub abs_diff_view_idx_minus1: u32, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct PredWeightTable { + pub luma_log2_weight_denom: u8, + pub chroma_log2_weight_denom: u8, + + pub luma_weight_l0: [i16; 32], + pub luma_offset_l0: [i8; 32], + + /* if seq->ChromaArrayType != 0 */ + pub chroma_weight_l0: [[i16; 2]; 32], + pub chroma_offset_l0: [[i8; 2]; 32], + + /* if slice->slice_type % 5 == 1 */ + pub luma_weight_l1: [i16; 32], + pub luma_offset_l1: [i16; 32], + + /* and if seq->ChromaArrayType != 0 */ + pub chroma_weight_l1: [[i16; 2]; 32], + pub chroma_offset_l1: [[i8; 2]; 32], +} + +/// Representation of `MaxLongTermFrameIdx`. +/// +/// `MaxLongTermFrameIdx` is derived from `max_long_term_frame_idx_plus1`, an unsigned integer with +/// a special value indicating "no long-term frame indices". This type allows easy conversion +/// between the actual and "plus1" representation, while ensuring that the special value is always +/// handled by the code. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub enum MaxLongTermFrameIdx { + #[default] + NoLongTermFrameIndices, + Idx(u32), +} + +impl MaxLongTermFrameIdx { + /// Create a value from `max_long_term_frame_idx_plus1`. + pub fn from_value_plus1(max_long_term_frame_idx_plus1: u32) -> Self { + match max_long_term_frame_idx_plus1 { + 0 => Self::NoLongTermFrameIndices, + i @ 1.. => Self::Idx(i - 1), + } + } + + /// Convert this value to the representation used by `max_long_term_frame_idx_plus1`. + pub fn to_value_plus1(self) -> u32 { + match self { + Self::NoLongTermFrameIndices => 0, + Self::Idx(i) => i + 1, + } + } +} + +impl PartialEq for MaxLongTermFrameIdx { + fn eq(&self, other: &u32) -> bool { + match self { + MaxLongTermFrameIdx::NoLongTermFrameIndices => false, + MaxLongTermFrameIdx::Idx(idx) => idx.eq(other), + } + } +} + +impl PartialOrd for MaxLongTermFrameIdx { + fn partial_cmp(&self, other: &u32) -> Option { + match self { + MaxLongTermFrameIdx::NoLongTermFrameIndices => Some(std::cmp::Ordering::Less), + MaxLongTermFrameIdx::Idx(idx) => Some(idx.cmp(other)), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct RefPicMarkingInner { + /// Specifies a control operation to be applied to affect the reference + /// picture marking. The `memory_management_control_operation` syntax element + /// is followed by data necessary for the operation specified by the value + /// of `memory_management_control_operation`. The values and control + /// operations associated with `memory_management_control_operation` are + /// specified in Table 7-9 + pub memory_management_control_operation: u8, + + /// Used (with memory_management_control_operation equal to 3 or 1) to + /// assign a long-term frame index to a short-term reference picture or to + /// mark a short-term reference picture as "unused for reference". + pub difference_of_pic_nums_minus1: u32, + + /// Used (with memory_management_control_operation equal to 2) to mark a + /// long-term reference picture as "unused for reference". + pub long_term_pic_num: u32, + + /// Used (with memory_management_control_operation equal to 3 or 6) to + /// assign a long-term frame index to a picture. + pub long_term_frame_idx: u32, + + /// Specifies the maximum value of long-term frame index allowed for + /// long-term reference pictures (until receipt of another value of + /// `max_long_term_frame_idx_plus1`). + pub max_long_term_frame_idx: MaxLongTermFrameIdx, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct RefPicMarking { + /// Specifies how the previously-decoded pictures in the decoded picture + /// buffer are treated after decoding of an IDR picture. See Annex C. + pub no_output_of_prior_pics_flag: bool, + + /// If unset, specifies that the MaxLongTermFrameIdx variable is set equal + /// to "no long-term frame indices" and that the IDR picture is marked as + /// "used for short-term reference". If set, specifies that the + /// MaxLongTermFrameIdx variable is set equal to 0 and that the current IDR + /// picture is marked "used for long-term reference" and is assigned + /// LongTermFrameIdx equal to 0. + pub long_term_reference_flag: bool, + + /// Selects the reference picture marking mode of the currently decoded + /// picture as specified in Table 7-8. + pub adaptive_ref_pic_marking_mode_flag: bool, + + /// An Vec with additional data used in the marking process. + pub inner: Vec, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SliceHeader { + /// Specifies the address of the first macroblock in the slice. + pub first_mb_in_slice: u32, + + /// Specifies the coding type of the slice according to Table 7-6. + pub slice_type: SliceType, + + // Specifies the picture parameter set in use + pub pic_parameter_set_id: u8, + + /// Specifies the colour plane associated with the current slice RBSP when + /// `separate_colour_plane_flag` is set. + pub colour_plane_id: u8, + + /// Used as an identifier for pictures and shall be represented by + /// `log2_max_frame_num_minus4 + 4` bits in the bitstream. + pub frame_num: u16, + + /// If set, specifies that the slice is a slice of a coded field. If not + /// set, specifies that the slice is a slice of a coded frame. + pub field_pic_flag: bool, + + /// If set, specifies that the slice is part of a coded bottom field. If not + /// set, specifies that the picture is a coded top field. + pub bottom_field_flag: bool, + + /// Identifies an IDR picture. The values of `idr_pic_id` in all the slices + /// of an IDR picture shall remain unchanged. When two consecutive access + /// units in decoding order are both IDR access units, the value of + /// `idr_pic_id` in the slices of the first such IDR access unit shall + /// differ from the `idr_pic_id` in the second such IDR access unit + pub idr_pic_id: u16, + + /// Specifies the picture order count modulo `MaxPicOrderCntLsb` for the top + /// field of a coded frame or for a coded field. The length of the + /// `pic_order_cnt_lsb` syntax element is + /// `log2_max_pic_order_cnt_lsb_minus4` + 4 bits. + pub pic_order_cnt_lsb: u16, + + /// Specifies the picture order count difference between the bottom field + /// and the top field of a coded frame as follows + pub delta_pic_order_cnt_bottom: i32, + + /// The first entry specifies the picture order count difference from the + /// expected picture order count for the top field of a coded frame or for a + /// coded field as specified in clause 8.2.1 The second entry specifies the + /// picture order count difference from the expected picture order count for + /// the bottom field of a coded frame specified in clause 8.2.1. + pub delta_pic_order_cnt: [i32; 2], + + /// This value is required by V4L2 stateless decode params so it is calculated + /// by parser while processing slice header. + pub pic_order_cnt_bit_size: usize, + + /// Shall be equal to 0 for slices and slice data partitions belonging to + /// the primary coded picture. The value of `redundant_pic_cnt shall` be + /// greater than 0 for coded slices or coded slice data partitions of a + /// redundant coded picture + pub redundant_pic_cnt: u8, + + /// Specifies the method used in the decoding process to derive motion + /// vectors and reference indices for inter prediction > + pub direct_spatial_mv_pred_flag: bool, + + /// If set, specifies that the syntax element `num_ref_idx_l0_active_minus1` + /// is present for P, SP, and B slices and that the syntax element + /// `num_ref_idx_l1_active_minus1` is present for B slices. If not set, + /// specifies that the syntax elements `num_ref_idx_l0_active_minus1` and + /// `num_ref_idx_l1_active_minus1` are not present. + pub num_ref_idx_active_override_flag: bool, + + /// Specifies the maximum reference index for reference picture list 0 that + /// shall be used to decode the slice. + pub num_ref_idx_l0_active_minus1: u8, + + /// Specifies the maximum reference index for reference picture list 1 that + /// shall be used to decode the slice. + pub num_ref_idx_l1_active_minus1: u8, + + /// If set, specifies that the syntax element `modification_of_pic_nums_idc` + /// is present for specifying reference picture list 0. If not set, + /// specifies that this syntax element is not present. + pub ref_pic_list_modification_flag_l0: bool, + + /// Reference picture list 0 modification as parsed with the + /// `ref_pic_list_modification()` process. + pub ref_pic_list_modification_l0: Vec, + + /// If set, specifies that the syntax element `modification_of_pic_nums_idc` + /// is present for specifying reference picture list 1. If not set, + /// specifies that this syntax element is not present. + pub ref_pic_list_modification_flag_l1: bool, + + /// Reference picture list 1 modification as parsed with the + /// `ref_pic_list_modification()` process. + pub ref_pic_list_modification_l1: Vec, + + /// Prediction weight table as parsed using 7.3.3.2 + pub pred_weight_table: PredWeightTable, + + /// Decoded reference picture marking parsed using 7.3.3.3 + pub dec_ref_pic_marking: RefPicMarking, + + /// This value is required by V4L2 stateless decode params so it is calculated + /// by parser while processing slice header. + pub dec_ref_pic_marking_bit_size: usize, + + /// Specifies the index for determining the initialization table used in the + /// initialization process for context variables. + pub cabac_init_idc: u8, + + /// Specifies the initial value of QP Y to be used for all the macroblocks + /// in the slice until modified by the value of `mb_qp_delta` in the + /// macroblock layer. The initial QPY quantization parameter for the slice + /// is computed using 7-30. + pub slice_qp_delta: i8, + + /// Specifies the decoding process to be used to decode P macroblocks in an + /// SP slice. + pub sp_for_switch_flag: bool, + + /// Specifies the value of QSY for all the macroblocks in SP and SI slices. + /// The QSY quantization parameter for the slice is computed using 7-31. + pub slice_qs_delta: i8, + + /// Specifies whether the operation of the deblocking filter shall be + /// disabled across some block edges of the slice and specifies for which + /// edges the filtering is disabled. + pub disable_deblocking_filter_idc: u8, + + /// Specifies the offset used in accessing the α and tC0 deblocking filter + /// tables for filtering operations controlled by the macroblocks within the + /// slice. From this value, the offset that shall be applied when addressing + /// these tables shall be computed using 7-32. + pub slice_alpha_c0_offset_div2: i8, + + /// Specifies the offset used in accessing the β deblocking filter table for + /// filtering operations controlled by the macroblocks within the slice. + /// From this value, the offset that is applied when addressing the β table + /// of the deblocking filter shall be computed using 7-33. + pub slice_beta_offset_div2: i8, + + /// Same as `MaxPicNum` in the specification. + pub max_pic_num: u32, + + /// Size of the slice_header() in bits + pub header_bit_size: usize, + + /// Number of emulation prevention bytes (EPB) in this slice_header() + pub n_emulation_prevention_bytes: usize, +} + +impl SliceHeader { + /// Returns the field that is coded by this header. + pub fn field(&self) -> Field { + if self.field_pic_flag { + if self.bottom_field_flag { + Field::Bottom + } else { + Field::Top + } + } else { + Field::Frame + } + } +} + +pub struct SliceHeaderBuilder(SliceHeader); + +impl SliceHeaderBuilder { + pub fn new(pps: &Pps) -> Self { + SliceHeaderBuilder(SliceHeader { + pic_parameter_set_id: pps.pic_parameter_set_id, + ..Default::default() + }) + } + + pub fn slice_type(mut self, type_: SliceType) -> Self { + self.0.slice_type = type_; + self + } + + pub fn first_mb_in_slice(mut self, value: u32) -> Self { + self.0.first_mb_in_slice = value; + self + } + + pub fn pic_order_cnt_lsb(mut self, value: u16) -> Self { + self.0.pic_order_cnt_lsb = value; + self + } + + pub fn idr_pic_id(mut self, value: u16) -> Self { + self.0.idr_pic_id = value; + self + } + + pub fn num_ref_idx_active_override_flag(mut self, value: bool) -> Self { + self.0.num_ref_idx_active_override_flag = value; + self + } + + pub fn num_ref_idx_l0_active_minus1(mut self, value: u8) -> Self { + self = self.num_ref_idx_active_override_flag(true); + self.0.num_ref_idx_l0_active_minus1 = value; + self + } + + pub fn num_ref_idx_l0_active(self, value: u8) -> Self { + self.num_ref_idx_l0_active_minus1(value - 1) + } + + pub fn num_ref_idx_l1_active_minus1(mut self, value: u8) -> Self { + self = self.num_ref_idx_active_override_flag(true); + self.0.num_ref_idx_l1_active_minus1 = value; + self + } + + pub fn num_ref_idx_l1_active(self, value: u8) -> Self { + self.num_ref_idx_l1_active_minus1(value - 1) + } + + pub fn build(self) -> SliceHeader { + self.0 + } +} + +/// A H264 slice. An integer number of macroblocks or macroblock pairs ordered +/// consecutively in the raster scan within a particular slice group +pub struct Slice<'a> { + /// The slice header. + pub header: SliceHeader, + /// The NAL unit backing this slice. + pub nalu: Nalu<'a>, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// See table 7-6 in the specification. +pub enum SliceType { + P = 0, + B = 1, + I = 2, + Sp = 3, + Si = 4, +} + +impl TryFrom for SliceType { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(SliceType::P), + 1 => Ok(SliceType::B), + 2 => Ok(SliceType::I), + 3 => Ok(SliceType::Sp), + 4 => Ok(SliceType::Si), + _ => Err(format!("Invalid SliceType {}", value)), + } + } +} + +impl SliceType { + /// Whether this is a P slice. See table 7-6 in the specification. + pub fn is_p(&self) -> bool { + matches!(self, SliceType::P) + } + + /// Whether this is a B slice. See table 7-6 in the specification. + pub fn is_b(&self) -> bool { + matches!(self, SliceType::B) + } + + /// Whether this is an I slice. See table 7-6 in the specification. + pub fn is_i(&self) -> bool { + matches!(self, SliceType::I) + } + + /// Whether this is a SP slice. See table 7-6 in the specification. + pub fn is_sp(&self) -> bool { + matches!(self, SliceType::Sp) + } + + /// Whether this is a SI slice. See table 7-6 in the specification. + pub fn is_si(&self) -> bool { + matches!(self, SliceType::Si) + } +} + +impl Default for SliceType { + fn default() -> Self { + Self::P + } +} + +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum Profile { + Baseline = 66, + Main = 77, + Extended = 88, + High = 100, + High10 = 110, + High422P = 122, +} + +impl TryFrom for Profile { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 66 => Ok(Profile::Baseline), + 77 => Ok(Profile::Main), + 88 => Ok(Profile::Extended), + 100 => Ok(Profile::High), + 110 => Ok(Profile::High10), + 122 => Ok(Profile::High422P), + _ => Err(format!("Invalid Profile {}", value)), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum Level { + #[default] + L1 = 10, + L1B = 9, + L1_1 = 11, + L1_2 = 12, + L1_3 = 13, + L2_0 = 20, + L2_1 = 21, + L2_2 = 22, + L3 = 30, + L3_1 = 31, + L3_2 = 32, + L4 = 40, + L4_1 = 41, + L4_2 = 42, + L5 = 50, + L5_1 = 51, + L5_2 = 52, + L6 = 60, + L6_1 = 61, + L6_2 = 62, +} + +impl TryFrom for Level { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 10 => Ok(Level::L1), + 9 => Ok(Level::L1B), + 11 => Ok(Level::L1_1), + 12 => Ok(Level::L1_2), + 13 => Ok(Level::L1_3), + 20 => Ok(Level::L2_0), + 21 => Ok(Level::L2_1), + 22 => Ok(Level::L2_2), + 30 => Ok(Level::L3), + 31 => Ok(Level::L3_1), + 32 => Ok(Level::L3_2), + 40 => Ok(Level::L4), + 41 => Ok(Level::L4_1), + 42 => Ok(Level::L4_2), + 50 => Ok(Level::L5), + 51 => Ok(Level::L5_1), + 52 => Ok(Level::L5_2), + 60 => Ok(Level::L6), + 61 => Ok(Level::L6_1), + 62 => Ok(Level::L6_2), + _ => Err(format!("Invalid Level {}", value)), + } + } +} + +/// A H264 Sequence Parameter Set. A syntax structure containing syntax elements +/// that apply to zero or more entire coded video sequences as determined by the +/// content of a seq_parameter_set_id syntax element found in the picture +/// parameter set referred to by the pic_parameter_set_id syntax element found +/// in each slice header. +#[derive(Debug, PartialEq, Eq)] +pub struct Sps { + /// Identifies the sequence parameter set that is referred to by the picture + /// parameter set + pub seq_parameter_set_id: u8, + + /// Profile to which the coded video sequence conforms + pub profile_idc: u8, + + /// Retains the same meaning as in the specification. See 7.4.2.1.1 + pub constraint_set0_flag: bool, + /// Retains the same meaning as in the specification. See 7.4.2.1.1 + pub constraint_set1_flag: bool, + /// Retains the same meaning as in the specification. See 7.4.2.1.1 + pub constraint_set2_flag: bool, + /// Retains the same meaning as in the specification. See 7.4.2.1.1 + pub constraint_set3_flag: bool, + /// Retains the same meaning as in the specification. See 7.4.2.1.1 + pub constraint_set4_flag: bool, + /// Retains the same meaning as in the specification. See 7.4.2.1.1 + pub constraint_set5_flag: bool, + + /// Level to which the coded video sequence conforms + pub level_idc: Level, + + /// Specifies the chroma sampling relative to the luma sampling as specified + /// in clause 6.2. + pub chroma_format_idc: u8, + + /// Specifies whether the three colour components of the 4:4:4 chroma format + /// are coded separately. + pub separate_colour_plane_flag: bool, + + /// Specifies the bit depth of the samples of the luma array and the value + /// of the luma quantization parameter range offset QpBdOffsetY. See 7-3 and + /// 7-4. + pub bit_depth_luma_minus8: u8, + + /// Specifies the bit depth of the samples of the chroma arrays and the + /// value of the chroma quantization parameter range offset QpBdOffsetC. See + /// 7-5 and 7-6. + pub bit_depth_chroma_minus8: u8, + + /// qpprime_y_zero_transform_bypass_flag equal to 1 specifies that, when + /// QP′Y is equal to 0, a transform bypass operation for the transform + /// coefficient decoding process and picture construction process prior to + /// deblocking filter process as specified in clause 8.5 shall be applied. + /// qpprime_y_zero_transform_bypass_flag equal to 0 specifies that the + /// transform coefficient decoding process and picture construction process + /// prior to deblocking filter process shall not use the transform bypass + /// operation + /// QP′Y is defined in 7-38 as QP′Y = QPY + QpBdOffsetY + pub qpprime_y_zero_transform_bypass_flag: bool, + + /// Whether `seq_scaling_list_present_flag[i]` for i = 0..7 or i = 0..11 is + /// present or whether the sequence level scaling list shall be specified by + /// Flat_4x4_16 for i = 0..5 and flat_8x8_16 for i = 6..11 + pub seq_scaling_matrix_present_flag: bool, + + /// 4x4 Scaling list as read with 7.3.2.1.1.1 + pub scaling_lists_4x4: [[u8; 16]; 6], + /// 8x8 Scaling list as read with 7.3.2.1.1.1 + pub scaling_lists_8x8: [[u8; 64]; 6], + + /// Specifies the value of the variable MaxFrameNum that is used in + /// frame_num related derivations as follows: MaxFrameNum = 2 ^ + /// (log2_max_frame_num_minus4 + 4 ) + pub log2_max_frame_num_minus4: u8, + + /// Specifies the method to decode picture order count (as specified in + /// clause 8.2.1) + pub pic_order_cnt_type: u8, + + /// Specifies the value of the variable MaxPicOrderCntLsb that is used in + /// the decoding process for picture order count as specified in clause + /// 8.2.1 as follows: MaxPicOrderCntLsb = 2 ^ ( + /// log2_max_pic_order_cnt_lsb_minus4 + 4 ). + pub log2_max_pic_order_cnt_lsb_minus4: u8, + + /// If true, specifies that `delta_pic_order_cnt[0]` and + /// `delta_pic_order_cnt[1]` are not present in the slice headers of the + /// sequence and shall be inferred to be equal to 0. + /// If false, specifies that `delta_pic_order_cnt[0]` is present in the + /// slice headers of the sequence and `delta_pic_order_cnt[1]` may be + /// present in the slice headers of the sequence. + pub delta_pic_order_always_zero_flag: bool, + + /// Used to calculate the picture order count of a non-reference picture as + /// specified in clause 8.2.1. + pub offset_for_non_ref_pic: i32, + + /// Used to calculate the picture order count of a bottom field as specified + /// in clause 8.2.1. + pub offset_for_top_to_bottom_field: i32, + + /// Used in the decoding process for picture order count as specified in + /// clause 8.2.1 + pub num_ref_frames_in_pic_order_cnt_cycle: u8, + + /// An element of a list of num_ref_frames_in_pic_order_cnt_cycle values + /// used in the decoding process for picture order count as specified in + /// clause 8.2. + pub offset_for_ref_frame: [i32; 255], + + /// Specifies the maximum number of short-term and long-term reference + /// frames, complementary reference field pairs, and non-paired reference + /// fields that may be used by the decoding process for inter prediction of + /// any picture in the coded video sequence. Also + /// determines the size of the sliding window operation as specified in + /// clause 8.2.5.3. + pub max_num_ref_frames: u8, + + /// Specifies the allowed values of frame_num as specified in clause 7.4.3 + /// and the decoding process in case of an inferred gap between values of + /// frame_num as specified in clause 8.2.5.2 + pub gaps_in_frame_num_value_allowed_flag: bool, + + /// Plus 1 specifies the width of each decoded picture in units of + /// macroblocks. + pub pic_width_in_mbs_minus1: u16, + /// Plus 1 specifies the height in slice group map units of a decoded frame + /// or field. + pub pic_height_in_map_units_minus1: u16, + + /// If true, specifies that every coded picture of the coded video sequence + /// is a coded frame containing only frame macroblocks, else specifies that + /// coded pictures of the coded video sequence may either be coded fields or + /// coded frames. + pub frame_mbs_only_flag: bool, + + /// If true, specifies the possible use of switching between frame and field + /// macroblocks within frames, else, specifies no switching between frame + /// and field macroblocks within a picture. + pub mb_adaptive_frame_field_flag: bool, + + /// Specifies the method used in the derivation process for luma motion + /// vectors for B_Skip, B_Direct_16x16 and B_Direct_8x8 as specified in + /// clause 8.4.1.2. + pub direct_8x8_inference_flag: bool, + + /// If true, specifies that the frame cropping offset parameters follow next + /// in the sequence parameter, else specifies that the frame cropping offset + /// parameters are not present + pub frame_cropping_flag: bool, + + /// Specify the samples of the pictures in the coded video sequence that are + /// output from the decoding process, in terms of a rectangular region + /// specified in frame coordinates for output. + pub frame_crop_left_offset: u32, + /// Specify the samples of the pictures in the coded video sequence that are + /// output from the decoding process, in terms of a rectangular region + /// specified in frame coordinates for output. + pub frame_crop_right_offset: u32, + /// Specify the samples of the pictures in the coded video sequence that are + /// output from the decoding process, in terms of a rectangular region + /// specified in frame coordinates for output. + pub frame_crop_top_offset: u32, + /// Specify the samples of the pictures in the coded video sequence that are + /// output from the decoding process, in terms of a rectangular region + /// specified in frame coordinates for output. + pub frame_crop_bottom_offset: u32, + + // Calculated + /// Same as ExpectedDeltaPerPicOrderCntCycle, see 7-12 in the specification. + pub expected_delta_per_pic_order_cnt_cycle: i32, + + pub vui_parameters_present_flag: bool, + pub vui_parameters: VuiParams, +} + +impl Sps { + /// Returns the coded width of the stream. + /// + /// See 7-13 through 7-17 in the specification. + pub const fn width(&self) -> u32 { + (self.pic_width_in_mbs_minus1 as u32 + 1) * 16 + } + + /// Returns the coded height of the stream. + /// + /// See 7-13 through 7-17 in the specification. + pub const fn height(&self) -> u32 { + (self.pic_height_in_map_units_minus1 as u32 + 1) + * 16 + * (2 - self.frame_mbs_only_flag as u32) + } + + /// Returns `ChromaArrayType`, as computed in the specification. + pub const fn chroma_array_type(&self) -> u8 { + match self.separate_colour_plane_flag { + false => self.chroma_format_idc, + true => 0, + } + } + + /// Returns `SubWidthC` and `SubHeightC`. + /// + /// See table 6-1 in the specification. + fn sub_width_height_c(&self) -> (u32, u32) { + match (self.chroma_format_idc, self.separate_colour_plane_flag) { + (1, false) => (2, 2), + (2, false) => (2, 1), + (3, false) => (1, 1), + // undefined. + _ => (1, 1), + } + } + + /// Returns `CropUnitX` and `CropUnitY`. + /// + /// See 7-19 through 7-22 in the specification. + fn crop_unit_x_y(&self) -> (u32, u32) { + match self.chroma_array_type() { + 0 => (1, 2 - u32::from(self.frame_mbs_only_flag)), + _ => { + let (sub_width_c, sub_height_c) = self.sub_width_height_c(); + (sub_width_c, sub_height_c * (2 - u32::from(self.frame_mbs_only_flag))) + } + } + } + + /// Same as MaxFrameNum. See 7-10 in the specification. + pub fn max_frame_num(&self) -> u32 { + 1 << (self.log2_max_frame_num_minus4 + 4) + } + + pub fn visible_rectangle(&self) -> Rect { + if !self.frame_cropping_flag { + return Rect { + min: Point { x: 0, y: 0 }, + max: Point { x: self.width(), y: self.height() }, + }; + } + + let (crop_unit_x, crop_unit_y) = self.crop_unit_x_y(); + + let crop_left = crop_unit_x * self.frame_crop_left_offset; + let crop_right = crop_unit_x * self.frame_crop_right_offset; + let crop_top = crop_unit_y * self.frame_crop_top_offset; + let crop_bottom = crop_unit_y * self.frame_crop_bottom_offset; + + Rect { + min: Point { x: crop_left, y: crop_top }, + max: Point { + x: self.width() - crop_left - crop_right, + y: self.height() - crop_top - crop_bottom, + }, + } + } + + pub fn max_dpb_frames(&self) -> usize { + let profile = self.profile_idc; + let mut level = self.level_idc; + + // A.3.1 and A.3.2: Level 1b for Baseline, Constrained Baseline and Main + // profile if level_idc == 11 and constraint_set3_flag == 1 + if matches!(level, Level::L1_1) + && (profile == Profile::Baseline as u8 || profile == Profile::Main as u8) + && self.constraint_set3_flag + { + level = Level::L1B; + }; + + // Table A.1 + let max_dpb_mbs = match level { + Level::L1 => 396, + Level::L1B => 396, + Level::L1_1 => 900, + Level::L1_2 => 2376, + Level::L1_3 => 2376, + Level::L2_0 => 2376, + Level::L2_1 => 4752, + Level::L2_2 => 8100, + Level::L3 => 8100, + Level::L3_1 => 18000, + Level::L3_2 => 20480, + Level::L4 => 32768, + Level::L4_1 => 32768, + Level::L4_2 => 34816, + Level::L5 => 110400, + Level::L5_1 => 184320, + Level::L5_2 => 184320, + Level::L6 => 696320, + Level::L6_1 => 696320, + Level::L6_2 => 696320, + }; + + let width_mb = self.width() / 16; + let height_mb = self.height() / 16; + + let max_dpb_frames = + std::cmp::min(max_dpb_mbs / (width_mb * height_mb), DPB_MAX_SIZE as u32) as usize; + + let mut max_dpb_frames = std::cmp::max(max_dpb_frames, self.max_num_ref_frames as usize); + + if self.vui_parameters_present_flag && self.vui_parameters.bitstream_restriction_flag { + max_dpb_frames = std::cmp::max(1, self.vui_parameters.max_dec_frame_buffering as usize); + } + + max_dpb_frames + } + + pub fn max_num_order_frames(&self) -> u32 { + let vui = &self.vui_parameters; + let present = self.vui_parameters_present_flag && vui.bitstream_restriction_flag; + + if present { + vui.max_num_reorder_frames + } else { + let profile = self.profile_idc; + if (profile == 44 + || profile == 86 + || profile == 100 + || profile == 110 + || profile == 122 + || profile == 244) + && self.constraint_set3_flag + { + 0 + } else { + self.max_dpb_frames() as u32 + } + } + } +} + +// TODO: Replace with builder +impl Default for Sps { + fn default() -> Self { + Self { + scaling_lists_4x4: [[0; 16]; 6], + scaling_lists_8x8: [[0; 64]; 6], + offset_for_ref_frame: [0; 255], + seq_parameter_set_id: Default::default(), + profile_idc: Default::default(), + constraint_set0_flag: Default::default(), + constraint_set1_flag: Default::default(), + constraint_set2_flag: Default::default(), + constraint_set3_flag: Default::default(), + constraint_set4_flag: Default::default(), + constraint_set5_flag: Default::default(), + level_idc: Default::default(), + chroma_format_idc: Default::default(), + separate_colour_plane_flag: Default::default(), + bit_depth_luma_minus8: Default::default(), + bit_depth_chroma_minus8: Default::default(), + qpprime_y_zero_transform_bypass_flag: Default::default(), + seq_scaling_matrix_present_flag: Default::default(), + log2_max_frame_num_minus4: Default::default(), + pic_order_cnt_type: Default::default(), + log2_max_pic_order_cnt_lsb_minus4: Default::default(), + delta_pic_order_always_zero_flag: Default::default(), + offset_for_non_ref_pic: Default::default(), + offset_for_top_to_bottom_field: Default::default(), + num_ref_frames_in_pic_order_cnt_cycle: Default::default(), + max_num_ref_frames: Default::default(), + gaps_in_frame_num_value_allowed_flag: Default::default(), + pic_width_in_mbs_minus1: Default::default(), + pic_height_in_map_units_minus1: Default::default(), + frame_mbs_only_flag: Default::default(), + mb_adaptive_frame_field_flag: Default::default(), + direct_8x8_inference_flag: Default::default(), + frame_cropping_flag: Default::default(), + frame_crop_left_offset: Default::default(), + frame_crop_right_offset: Default::default(), + frame_crop_top_offset: Default::default(), + frame_crop_bottom_offset: Default::default(), + expected_delta_per_pic_order_cnt_cycle: Default::default(), + vui_parameters_present_flag: Default::default(), + vui_parameters: Default::default(), + } + } +} + +#[derive(Default)] +pub struct SpsBuilder(Sps); + +impl SpsBuilder { + pub fn new() -> Self { + Default::default() + } + + pub fn seq_parameter_set_id(mut self, value: u8) -> Self { + self.0.seq_parameter_set_id = value; + self + } + + pub fn profile_idc(mut self, value: Profile) -> Self { + self.0.profile_idc = value as u8; + self + } + + pub fn level_idc(mut self, value: Level) -> Self { + self.0.level_idc = value; + self + } + + pub fn frame_crop_offsets(mut self, top: u32, bottom: u32, left: u32, right: u32) -> Self { + self.0.frame_cropping_flag = true; + self.0.frame_crop_top_offset = top; + self.0.frame_crop_bottom_offset = bottom; + self.0.frame_crop_left_offset = left; + self.0.frame_crop_right_offset = right; + self + } + + pub fn frame_crop(self, top: u32, bottom: u32, left: u32, right: u32) -> Self { + let sub_width_c = if self.0.chroma_format_idc > 2 { 1 } else { 2 }; + let sub_height_c = if self.0.chroma_format_idc > 1 { 1 } else { 2 }; + + let crop_unit_x = sub_width_c; + let crop_unit_y = sub_height_c * (if self.0.frame_mbs_only_flag { 1 } else { 2 }); + + self.frame_crop_offsets( + top / crop_unit_y, + bottom / crop_unit_y, + left / crop_unit_x, + right / crop_unit_x, + ) + } + + pub fn resolution(mut self, width: u32, height: u32) -> Self { + const MB_SIZE: u32 = 16; + + let mb_width = (width + MB_SIZE - 1) / MB_SIZE; + let mb_height = (height + MB_SIZE - 1) / MB_SIZE; + + self.0.pic_width_in_mbs_minus1 = (mb_width - 1) as u16; + self.0.pic_height_in_map_units_minus1 = (mb_height - 1) as u16; + + let compressed_width = mb_width * MB_SIZE; + let compressed_height = mb_height * MB_SIZE; + + if compressed_width != width || compressed_height != height { + self = self.frame_crop(0, compressed_height - height, 0, compressed_width - width); + } + + self + } + + pub fn chroma_format_idc(mut self, value: u8) -> Self { + self.0.chroma_format_idc = value; + self + } + + pub fn max_num_ref_frames(mut self, value: u8) -> Self { + self.0.max_num_ref_frames = value; + self + } + + pub fn frame_mbs_only_flag(mut self, value: bool) -> Self { + self.0.frame_mbs_only_flag = value; + self + } + + pub fn mb_adaptive_frame_field_flag(mut self, value: bool) -> Self { + self.0.mb_adaptive_frame_field_flag = value; + self + } + + pub fn seq_scaling_matrix_present_flag(mut self, value: bool) -> Self { + self.0.seq_scaling_matrix_present_flag = value; + self + } + + pub fn direct_8x8_inference_flag(mut self, value: bool) -> Self { + self.0.direct_8x8_inference_flag = value; + self + } + + pub fn vui_parameters_present(mut self) -> Self { + if self.0.vui_parameters_present_flag { + return self; + } + + self.0.vui_parameters_present_flag = true; + // Disable all options at default + self.0.vui_parameters.aspect_ratio_info_present_flag = false; + self.0.vui_parameters.overscan_info_present_flag = false; + self.0.vui_parameters.video_signal_type_present_flag = false; + self.0.vui_parameters.colour_description_present_flag = false; + self.0.vui_parameters.chroma_loc_info_present_flag = false; + self.0.vui_parameters.timing_info_present_flag = false; + self.0.vui_parameters.nal_hrd_parameters_present_flag = false; + self.0.vui_parameters.vcl_hrd_parameters_present_flag = false; + self.0.vui_parameters.pic_struct_present_flag = false; + self.0.vui_parameters.bitstream_restriction_flag = false; + self + } + + pub fn aspect_ratio_idc(mut self, value: u8) -> Self { + self = self.vui_parameters_present(); + self.0.vui_parameters.aspect_ratio_info_present_flag = true; + self.0.vui_parameters.aspect_ratio_idc = value; + self + } + + pub fn sar_resolution(mut self, width: u16, height: u16) -> Self { + self = self.aspect_ratio_idc(255); + self.0.vui_parameters.sar_width = width; + self.0.vui_parameters.sar_height = height; + self + } + + pub fn aspect_ratio(self, width_ratio: u16, height_ratio: u16) -> Self { + // H.264 Table E-1 + match (width_ratio, height_ratio) { + (1, 1) => self.aspect_ratio_idc(1), + (12, 11) => self.aspect_ratio_idc(2), + (10, 11) => self.aspect_ratio_idc(3), + (16, 11) => self.aspect_ratio_idc(4), + (40, 33) => self.aspect_ratio_idc(5), + (24, 11) => self.aspect_ratio_idc(6), + (20, 11) => self.aspect_ratio_idc(7), + (32, 11) => self.aspect_ratio_idc(8), + (80, 33) => self.aspect_ratio_idc(9), + (18, 11) => self.aspect_ratio_idc(10), + (15, 11) => self.aspect_ratio_idc(11), + (64, 33) => self.aspect_ratio_idc(12), + (160, 99) => self.aspect_ratio_idc(13), + (4, 3) => self.aspect_ratio_idc(14), + (3, 2) => self.aspect_ratio_idc(15), + (2, 1) => self.aspect_ratio_idc(16), + + _ => self.sar_resolution(width_ratio, height_ratio), + } + } + + pub fn timing_info( + mut self, + num_units_in_tick: u32, + time_scale: u32, + fixed_frame_rate_flag: bool, + ) -> Self { + self = self.vui_parameters_present(); + self.0.vui_parameters.timing_info_present_flag = true; + self.0.vui_parameters.num_units_in_tick = num_units_in_tick; + self.0.vui_parameters.time_scale = time_scale; + self.0.vui_parameters.fixed_frame_rate_flag = fixed_frame_rate_flag; + self + } + + pub fn log2_max_frame_num_minus4(mut self, value: u8) -> Self { + self.0.log2_max_frame_num_minus4 = value; + self + } + + pub fn max_frame_num(self, value: u32) -> Self { + self.log2_max_frame_num_minus4(value.ilog2() as u8 - 4u8) + } + + pub fn pic_order_cnt_type(mut self, value: u8) -> Self { + self.0.pic_order_cnt_type = value; + self + } + + pub fn log2_max_pic_order_cnt_lsb_minus4(mut self, value: u8) -> Self { + self.0.log2_max_pic_order_cnt_lsb_minus4 = value; + self + } + + pub fn max_pic_order_cnt_lsb(self, value: u32) -> Self { + self.log2_max_pic_order_cnt_lsb_minus4(value.ilog2() as u8 - 4u8) + } + + pub fn delta_pic_order_always_zero_flag(mut self, value: bool) -> Self { + self.0.delta_pic_order_always_zero_flag = value; + self + } + + pub fn bit_depth_chroma_minus8(mut self, value: u8) -> Self { + self.0.bit_depth_chroma_minus8 = value; + self + } + + pub fn bit_depth_chroma(self, value: u8) -> Self { + self.bit_depth_luma_minus8(value - 8u8) + } + + pub fn bit_depth_luma_minus8(mut self, value: u8) -> Self { + self.0.bit_depth_luma_minus8 = value; + self + } + + pub fn bit_depth_luma(self, value: u8) -> Self { + self.bit_depth_luma_minus8(value - 8u8) + } + + pub fn build(self) -> Rc { + Rc::new(self.0) + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct HrdParams { + /// Plus 1 specifies the number of alternative CPB specifications in the + /// bitstream. The value of `cpb_cnt_minus1` shall be in the range of 0 to 31, + /// inclusive + pub cpb_cnt_minus1: u8, + /// Together with `bit_rate_value_minus1[ SchedSelIdx ]` specifies the + /// maximum input bit rate of the `SchedSelIdx`-th CPB. + pub bit_rate_scale: u8, + /// Together with `cpb_size_value_minus1[ SchedSelIdx ]` specifies the CPB + /// size of the SchedSelIdx-th CPB. + pub cpb_size_scale: u8, + + /// `[ SchedSelIdx ]` (together with bit_rate_scale) specifies the maximum + /// input bit rate for the SchedSelIdx-th CPB. + pub bit_rate_value_minus1: [u32; 32], + /// `[ SchedSelIdx ]` is used together with cpb_size_scale to specify the + /// SchedSelIdx-th CPB size. + pub cpb_size_value_minus1: [u32; 32], + /// `[ SchedSelIdx ]` equal to 0 specifies that to decode this bitstream by + /// the HRD using the `SchedSelIdx`-th CPB specification, the hypothetical + /// stream delivery scheduler (HSS) operates in an intermittent bit rate + /// mode. `cbr_flag[ SchedSelIdx ]` equal to 1 specifies that the HSS operates + /// in a constant bit rate (CBR) mode + pub cbr_flag: [bool; 32], + + /// Specifies the length in bits of the `initial_cpb_removal_delay[ + /// SchedSelIdx ]` and `initial_cpb_removal_delay_offset[ SchedSelIdx ]` syntax + /// elements of the buffering period SEI message. + pub initial_cpb_removal_delay_length_minus1: u8, + /// Specifies the length in bits of the `cpb_removal_delay` syntax element. + pub cpb_removal_delay_length_minus1: u8, + /// Specifies the length in bits of the `dpb_output_delay` syntax element. + pub dpb_output_delay_length_minus1: u8, + /// If greater than 0, specifies the length in bits of the `time_offset` + /// syntax element. `time_offset_length` equal to 0 specifies that the + /// `time_offset` syntax element is not present + pub time_offset_length: u8, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VuiParams { + /// Specifies whether `aspect_ratio_idc` is present. + pub aspect_ratio_info_present_flag: bool, + /// Specifies the value of the sample aspect ratio of the luma samples. + /// Table E-1 shows the meaning of the code. When aspect_ratio_idc indicates + /// Extended_SAR, the sample aspect ratio is represented by sar_width : + /// sar_height. When the aspect_ratio_idc syntax element is not present, + /// aspect_ratio_idc value shall be inferred to be equal to 0 + pub aspect_ratio_idc: u8, + + /* if aspect_ratio_idc == 255 */ + /// Indicates the horizontal size of the sample aspect ratio (in arbitrary + /// units) + pub sar_width: u16, + /// Indicates the vertical size of the sample aspect ratio (in the same + /// arbitrary units as sar_width). + pub sar_height: u16, + + /// If true specifies that the overscan_appropriate_flag is present. Else, + /// the preferred display method for the video signal is unspecified + pub overscan_info_present_flag: bool, + /* if overscan_info_present_flag */ + /// If true, indicates that the cropped decoded pictures output are suitable + /// for display using overscan. Else, indicates that the cropped decoded + /// pictures output contain visually important information in the entire + /// region out to the edges of the cropping rectangle of the picture, such + /// that the cropped decoded pictures output should not be displayed using + /// overscan. + pub overscan_appropriate_flag: bool, + + /// Specifies that video_format, video_full_range_flag and + /// colour_description_present_flag are present + pub video_signal_type_present_flag: bool, + /// Indicates the representation of the pictures as specified in Table E-2, + /// before being coded in accordance with this Recommendation | + /// International Standard. When the video_format syntax element is not + /// present, video_format value shall be inferred to be equal to 5. + pub video_format: u8, + /// Indicates the black level and range of the luma and chroma signals as + /// derived from E′Y, E′PB, and E′PR or E′ R, E′G, and E′B real-valued + /// component signals. + pub video_full_range_flag: bool, + /// Specifies that colour_primaries, transfer_characteristics and + /// matrix_coefficients are present. + pub colour_description_present_flag: bool, + /// Indicates the chromaticity coordinates of the source primaries as + /// specified in Table E-3 in terms of the CIE 1931 definition of x and y as + /// specified by ISO 11664-1. + pub colour_primaries: u8, + /// Retains same meaning as in the specification. + pub transfer_characteristics: u8, + /// Describes the matrix coefficients used in deriving luma and chroma + /// signals from the green, blue, and red, or Y, Z, and X primaries, as + /// specified in Table E-5. + pub matrix_coefficients: u8, + + /// Specifies that chroma_sample_loc_type_top_field and + /// chroma_sample_loc_type_bottom_field are present + pub chroma_loc_info_present_flag: bool, + /// Specify the location of chroma samples. See the spec for more details. + pub chroma_sample_loc_type_top_field: u8, + /// Specify the location of chroma samples. See the spec for more details. + pub chroma_sample_loc_type_bottom_field: u8, + + /// Specifies that num_units_in_tick, time_scale and fixed_frame_rate_flag + /// are present in the bitstream + pub timing_info_present_flag: bool, + /* if timing_info_present_flag */ + /// The number of time units of a clock operating at the frequency + /// time_scale Hz that corresponds to one increment (called a clock tick) of + /// a clock tick counter + pub num_units_in_tick: u32, + /// The number of time units that pass in one second. For example, a time + /// coordinate system that measures time using a 27 MHz clock has a + /// time_scale of 27 000 000. time_scale shall be greater than 0. + pub time_scale: u32, + /// Retains the same meaning as the specification. + pub fixed_frame_rate_flag: bool, + + /// Specifies that NAL HRD parameters (pertaining to Type II bitstream + /// conformance) are present. + pub nal_hrd_parameters_present_flag: bool, + /* if nal_hrd_parameters_present_flag */ + /// The NAL HDR parameters + pub nal_hrd_parameters: HrdParams, + /// Specifies that VCL HRD parameters (pertaining to all bitstream + /// conformance) are present. + pub vcl_hrd_parameters_present_flag: bool, + /* if vcl_hrd_parameters_present_flag */ + /// The VCL HRD parameters + pub vcl_hrd_parameters: HrdParams, + + /// Specifies the HRD operational mode as specified in Annex C. + pub low_delay_hrd_flag: bool, + + /// Specifies that picture timing SEI messages (clause D.2.3) are present + /// that include the pic_struct syntax element. + pub pic_struct_present_flag: bool, + + /// Specifies that the following coded video sequence bitstream restriction + /// parameters are present + pub bitstream_restriction_flag: bool, + /* if bitstream_restriction_flag */ + /// If false, indicates that no sample outside the picture boundaries and no + /// sample at a fractional sample position for which the sample value is + /// derived using one or more samples outside the picture boundaries is used + /// for inter prediction of any sample. If true, indicates that one or more + /// samples outside picture boundaries may be used in inter prediction. When + /// the motion_vectors_over_pic_boundaries_flag syntax element is not + /// present, motion_vectors_over_pic_boundaries_flag value shall be inferred + /// to be true. + pub motion_vectors_over_pic_boundaries_flag: bool, + /// Indicates a number of bytes not exceeded by the sum of the sizes of the + /// VCL NAL units associated with any coded picture in the coded video + /// sequence. + pub max_bytes_per_pic_denom: u32, + /// Indicates an upper bound for the number of coded bits of + /// macroblock_layer( ) data for any macroblock in any picture of the coded + /// video sequence + pub max_bits_per_mb_denom: u32, + /// Retains the same meaning as the specification. + pub log2_max_mv_length_horizontal: u32, + /// Retains the same meaning as the specification. + pub log2_max_mv_length_vertical: u32, + /// Indicates an upper bound for the number of frames buffers, in the + /// decoded picture buffer (DPB), that are required for storing frames, + /// complementary field pairs, and non-paired fields before output. It is a + /// requirement of bitstream conformance that the maximum number of frames, + /// complementary field pairs, or non-paired fields that precede any frame, + /// complementary field pair, or non-paired field in the coded video + /// sequence in decoding order and follow it in output order shall be less + /// than or equal to max_num_reorder_frames. The value of + /// max_num_reorder_frames shall be in the range of 0 to + /// max_dec_frame_buffering, inclusive. + /// + /// When the max_num_reorder_frames syntax element is not present, the value + /// of max_num_reorder_frames value shall be inferred as follows: + /// If profile_idc is equal to 44, 86, 100, 110, 122, or 244 and + /// constraint_set3_flag is equal to 1, the value of max_num_reorder_frames + /// shall be inferred to be equal to 0. + /// + /// Otherwise (profile_idc is not equal to 44, 86, 100, 110, 122, or 244 or + /// constraint_set3_flag is equal to 0), the value of max_num_reorder_frames + /// shall be inferred to be equal to MaxDpbFrames. + pub max_num_reorder_frames: u32, + /// Specifies the required size of the HRD decoded picture buffer (DPB) in + /// units of frame buffers. It is a requirement of bitstream conformance + /// that the coded video sequence shall not require a decoded picture buffer + /// with size of more than Max( 1, max_dec_frame_buffering ) frame buffers + /// to enable the output of decoded pictures at the output times specified + /// by dpb_output_delay of the picture timing SEI messages. The value of + /// max_dec_frame_buffering shall be greater than or equal to + /// max_num_ref_frames. An upper bound for the value of + /// max_dec_frame_buffering is specified by the level limits in clauses + /// A.3.1, A.3.2, G.10.2.1, and H.10.2. + /// + /// When the max_dec_frame_buffering syntax element is not present, the + /// value of max_dec_frame_buffering shall be inferred as follows: + /// + /// If profile_idc is equal to 44, 86, 100, 110, 122, or 244 and + /// constraint_set3_flag is equal to 1, the value of max_dec_frame_buffering + /// shall be inferred to be equal to 0. + /// + /// Otherwise (profile_idc is not equal to 44, 86, 100, 110, 122, or 244 or + /// constraint_set3_flag is equal to 0), the value of + /// max_dec_frame_buffering shall be inferred to be equal to MaxDpbFrames. + pub max_dec_frame_buffering: u32, +} + +impl Default for VuiParams { + fn default() -> Self { + Self { + aspect_ratio_info_present_flag: Default::default(), + aspect_ratio_idc: Default::default(), + sar_width: Default::default(), + sar_height: Default::default(), + overscan_info_present_flag: Default::default(), + overscan_appropriate_flag: Default::default(), + video_signal_type_present_flag: Default::default(), + video_format: 5, + video_full_range_flag: Default::default(), + colour_description_present_flag: Default::default(), + colour_primaries: 2, + transfer_characteristics: 2, + matrix_coefficients: 2, + chroma_loc_info_present_flag: Default::default(), + chroma_sample_loc_type_top_field: Default::default(), + chroma_sample_loc_type_bottom_field: Default::default(), + timing_info_present_flag: Default::default(), + num_units_in_tick: Default::default(), + time_scale: Default::default(), + fixed_frame_rate_flag: Default::default(), + nal_hrd_parameters_present_flag: Default::default(), + nal_hrd_parameters: Default::default(), + vcl_hrd_parameters_present_flag: Default::default(), + vcl_hrd_parameters: Default::default(), + low_delay_hrd_flag: Default::default(), + pic_struct_present_flag: Default::default(), + bitstream_restriction_flag: Default::default(), + motion_vectors_over_pic_boundaries_flag: Default::default(), + max_bytes_per_pic_denom: Default::default(), + max_bits_per_mb_denom: Default::default(), + log2_max_mv_length_horizontal: Default::default(), + log2_max_mv_length_vertical: Default::default(), + max_num_reorder_frames: Default::default(), + max_dec_frame_buffering: Default::default(), + } + } +} + +/// A H264 Picture Parameter Set. A syntax structure containing syntax elements +/// that apply to zero or more entire coded pictures as determined by the +/// `pic_parameter_set_id` syntax element found in each slice header. +#[derive(Debug, PartialEq, Eq)] +pub struct Pps { + /// Identifies the picture parameter set that is referred to in the slice header. + pub pic_parameter_set_id: u8, + + /// Refers to the active sequence parameter set. + pub seq_parameter_set_id: u8, + + /// Selects the entropy decoding method to be applied for the syntax + /// elements for which two descriptors appear in the syntax tables as + /// follows: If `entropy_coding_mode_flag` is false, the method specified by + /// the left descriptor in the syntax table is applied (Exp-Golomb coded, + /// see clause 9.1 or CAVLC, see clause 9.2). Otherwise + /// (`entropy_coding_mode_flag` is true), the method specified by the right + /// descriptor in the syntax table is applied (CABAC, see clause 9.3). + pub entropy_coding_mode_flag: bool, + + /// If true, specifies that the syntax elements delta_pic_order_cnt_bottom + /// (when `pic_order_cnt_type` is equal to 0) or `delta_pic_order_cnt[1]` + /// (when `pic_order_cnt_type` is equal to 1), which are related to picture + /// order counts for the bottom field of a coded frame, are present in the + /// slice headers for coded frames as specified in clause 7.3.3. Otherwise, + /// specifies that the syntax elements `delta_pic_order_cnt_bottom` and + /// `delta_pic_order_cnt[1]` are not present in the slice headers. + pub bottom_field_pic_order_in_frame_present_flag: bool, + + /// Plus 1 specifies the number of slice groups for a picture. When + /// `num_slice_groups_minus1` is equal to 0, all slices of the picture + /// belong to the same slice group. The allowed range of + /// `num_slice_groups_minus1` is specified in Annex A. + pub num_slice_groups_minus1: u32, + + /// Specifies how `num_ref_idx_l0_active_minus1` is inferred for P, SP, and + /// B slices with `num_ref_idx_active_override_flag` not set. + pub num_ref_idx_l0_default_active_minus1: u8, + + /// Specifies how `num_ref_idx_l1_active_minus1` is inferred for B slices + /// with `num_ref_idx_active_override_flag` not set. + pub num_ref_idx_l1_default_active_minus1: u8, + + /// If not set, specifies that the default weighted prediction shall be + /// applied to P and SP slices. If set, specifies that explicit weighted + /// prediction shall be applied to P and SP slices. + pub weighted_pred_flag: bool, + + /// `weighted_bipred_idc` equal to 0 specifies that the default weighted + /// prediction shall be applied to B slices. `weighted_bipred_idc` equal to + /// 1 specifies that explicit weighted prediction shall be applied to B + /// slices. `weighted_bipred_idc` equal to 2 specifies that implicit + /// weighted prediction shall be applied to B slices + pub weighted_bipred_idc: u8, + + /// Specifies the initial value minus 26 of SliceQPY for each slice. The + /// initial value is modified at the slice layer when a non-zero value of + /// `slice_qp_delta` is decoded, and is modified further when a non-zero + /// value of `mb_qp_delta` is decoded at the macroblock layer. + pub pic_init_qp_minus26: i8, + + /// Specifies the initial value minus 26 of SliceQSY for all macroblocks in + /// SP or SI slices. The initial value is modified at the slice layer when a + /// non-zero value of `slice_qs_delta` is decoded. + pub pic_init_qs_minus26: i8, + + /// Specifies the offset that shall be added to QP Y and QSY for addressing + /// the table of QPC values for the Cb chroma component. + pub chroma_qp_index_offset: i8, + + /// If set, specifies that a set of syntax elements controlling the + /// characteristics of the deblocking filter is present in the slice header. + /// If not set, specifies that the set of syntax elements controlling the + /// characteristics of the deblocking filter is not present in the slice + /// headers and their inferred values are in effect. + pub deblocking_filter_control_present_flag: bool, + + /// If not set, specifies that intra prediction allows usage of residual + /// data and decoded samples of neighbouring macroblocks coded using Inter + /// macroblock prediction modes for the prediction of macroblocks coded + /// using Intra macroblock prediction modes. If set, specifies constrained + /// intra prediction, in which case prediction of macroblocks coded using + /// Intra macroblock prediction modes only uses residual data and decoded + /// samples from I or SI macroblock types. + pub constrained_intra_pred_flag: bool, + + /// If not set, specifies that the `redundant_pic_cnt` syntax element is not + /// present in slice headers, coded slice data partition B NAL units, and + /// coded slice data partition C NAL units that refer (either directly or by + /// association with a corresponding coded slice data partition A NAL unit) + /// to the picture parameter set. If set, specifies that the + /// `redundant_pic_cnt` syntax element is present in all slice headers, + /// coded slice data partition B NAL units, and coded slice data partition C + /// NAL units that refer (either directly or by association with a + /// corresponding coded slice data partition A NAL unit) to the picture + /// parameter set. + pub redundant_pic_cnt_present_flag: bool, + + /// If set, specifies that the 8x8 transform decoding process may be in use + /// (see clause 8.5). If not set, specifies that the 8x8 transform decoding + /// process is not in use. + pub transform_8x8_mode_flag: bool, + + /// If set, specifies that parameters are present to modify the scaling + /// lists specified in the sequence parameter set. If not set, specifies + /// that the scaling lists used for the picture shall be inferred to be + /// equal to those specified by the sequence parameter set. + pub pic_scaling_matrix_present_flag: bool, + + /// 4x4 Scaling list as read with 7.3.2.1.1.1 + pub scaling_lists_4x4: [[u8; 16]; 6], + /// 8x8 Scaling list as read with 7.3.2.1.1.1 + pub scaling_lists_8x8: [[u8; 64]; 6], + + /// Specifies the offset that shall be added to QPY and QSY for addressing + /// the table of QPC values for the Cr chroma component. When + /// `second_chroma_qp_index_offset` is not present, it shall be inferred to be + /// equal to `chroma_qp_index_offset`. + pub second_chroma_qp_index_offset: i8, + + /// The SPS referenced by this PPS. + pub sps: Rc, +} + +pub struct PpsBuilder(Pps); + +impl PpsBuilder { + pub fn new(sps: Rc) -> Self { + PpsBuilder(Pps { + pic_parameter_set_id: 0, + seq_parameter_set_id: sps.seq_parameter_set_id, + entropy_coding_mode_flag: false, + bottom_field_pic_order_in_frame_present_flag: false, + num_slice_groups_minus1: 0, + num_ref_idx_l0_default_active_minus1: 0, + num_ref_idx_l1_default_active_minus1: 0, + weighted_pred_flag: false, + weighted_bipred_idc: 0, + pic_init_qp_minus26: 0, + pic_init_qs_minus26: 0, + chroma_qp_index_offset: 0, + deblocking_filter_control_present_flag: false, + constrained_intra_pred_flag: false, + redundant_pic_cnt_present_flag: false, + transform_8x8_mode_flag: false, + pic_scaling_matrix_present_flag: false, + scaling_lists_4x4: [[0; 16]; 6], + scaling_lists_8x8: [[0; 64]; 6], + second_chroma_qp_index_offset: 0, + sps, + }) + } + + pub fn pic_parameter_set_id(mut self, value: u8) -> Self { + self.0.pic_parameter_set_id = value; + self + } + + pub fn pic_init_qp_minus26(mut self, value: i8) -> Self { + self.0.pic_init_qp_minus26 = value; + self + } + + pub fn pic_init_qp(self, value: u8) -> Self { + self.pic_init_qp_minus26(value as i8 - 26) + } + + pub fn deblocking_filter_control_present_flag(mut self, value: bool) -> Self { + self.0.deblocking_filter_control_present_flag = value; + self + } + + pub fn num_ref_idx_l0_default_active_minus1(mut self, value: u8) -> Self { + self.0.num_ref_idx_l0_default_active_minus1 = value; + self + } + + pub fn num_ref_idx_l0_default_active(self, value: u8) -> Self { + self.num_ref_idx_l0_default_active_minus1(value - 1) + } + + pub fn num_ref_idx_l1_default_active_minus1(mut self, value: u8) -> Self { + self.0.num_ref_idx_l1_default_active_minus1 = value; + self + } + + pub fn num_ref_idx_l1_default_active(self, value: u8) -> Self { + self.num_ref_idx_l1_default_active_minus1(value - 1) + } + + pub fn build(self) -> Rc { + Rc::new(self.0) + } +} + +#[derive(Debug, Default)] +pub struct Parser { + active_spses: BTreeMap>, + active_ppses: BTreeMap>, +} + +impl Parser { + fn fill_default_scaling_list_4x4(scaling_list4x4: &mut [u8; 16], i: usize) { + // See table 7.2 in the spec. + assert!(i < 6); + if i < 3 { + *scaling_list4x4 = DEFAULT_4X4_INTRA; + } else if i < 6 { + *scaling_list4x4 = DEFAULT_4X4_INTER; + } + } + + fn fill_default_scaling_list_8x8(scaling_list8x8: &mut [u8; 64], i: usize) { + assert!(i < 6); + if i % 2 == 0 { + *scaling_list8x8 = DEFAULT_8X8_INTRA; + } else { + *scaling_list8x8 = DEFAULT_8X8_INTER; + } + } + + fn fill_fallback_scaling_list_4x4( + scaling_list4x4: &mut [[u8; 16]; 6], + i: usize, + default_scaling_list_intra: &[u8; 16], + default_scaling_list_inter: &[u8; 16], + ) { + // See table 7.2 in the spec. + scaling_list4x4[i] = match i { + 0 => *default_scaling_list_intra, + 1 => scaling_list4x4[0], + 2 => scaling_list4x4[1], + 3 => *default_scaling_list_inter, + 4 => scaling_list4x4[3], + 5 => scaling_list4x4[4], + _ => panic!("Unexpected value {}", i), + } + } + + fn fill_fallback_scaling_list_8x8( + scaling_list8x8: &mut [[u8; 64]; 6], + i: usize, + default_scaling_list_intra: &[u8; 64], + default_scaling_list_inter: &[u8; 64], + ) { + // See table 7.2 in the spec. + scaling_list8x8[i] = match i { + 0 => *default_scaling_list_intra, + 1 => *default_scaling_list_inter, + 2 => scaling_list8x8[0], + 3 => scaling_list8x8[1], + 4 => scaling_list8x8[2], + 5 => scaling_list8x8[3], + _ => panic!("Unexpected value {}", i), + } + } + + fn fill_scaling_list_flat( + scaling_list4x4: &mut [[u8; 16]; 6], + scaling_list8x8: &mut [[u8; 64]; 6], + ) { + // (7-8) in the spec. + for outer in scaling_list4x4 { + for inner in outer { + *inner = 16; + } + } + + // (7-9) in the spec. + for outer in scaling_list8x8 { + for inner in outer { + *inner = 16; + } + } + } + + fn parse_scaling_list>( + r: &mut BitReader, + scaling_list: &mut U, + use_default: &mut bool, + ) -> Result<(), String> { + // 7.3.2.1.1.1 + let mut last_scale = 8u8; + let mut next_scale = 8u8; + + for j in 0..scaling_list.as_mut().len() { + if next_scale != 0 { + let delta_scale = r.read_se::()?; + next_scale = ((last_scale as i32 + delta_scale + 256) % 256) as u8; + *use_default = j == 0 && next_scale == 0; + if *use_default { + return Ok(()); + } + } + + scaling_list.as_mut()[j] = if next_scale == 0 { last_scale } else { next_scale }; + + last_scale = scaling_list.as_mut()[j]; + } + + Ok(()) + } + + fn parse_sps_scaling_lists(r: &mut BitReader, sps: &mut Sps) -> Result<(), String> { + let scaling_lists4x4 = &mut sps.scaling_lists_4x4; + let scaling_lisst8x8 = &mut sps.scaling_lists_8x8; + + // Parse scaling_list4x4 + for i in 0..6 { + let seq_scaling_list_present_flag = r.read_bit()?; + if seq_scaling_list_present_flag { + let mut use_default = false; + + Parser::parse_scaling_list(r, &mut scaling_lists4x4[i], &mut use_default)?; + + if use_default { + Parser::fill_default_scaling_list_4x4(&mut scaling_lists4x4[i], i); + } + } else { + Parser::fill_fallback_scaling_list_4x4( + scaling_lists4x4, + i, + &DEFAULT_4X4_INTRA, + &DEFAULT_4X4_INTER, + ); + } + } + + // Parse scaling_list8x8 + let num_8x8 = if sps.chroma_format_idc != 3 { 2 } else { 6 }; + for i in 0..num_8x8 { + let seq_scaling_list_present_flag = r.read_bit()?; + if seq_scaling_list_present_flag { + let mut use_default = false; + Parser::parse_scaling_list(r, &mut scaling_lisst8x8[i], &mut use_default)?; + + if use_default { + Parser::fill_default_scaling_list_8x8(&mut scaling_lisst8x8[i], i); + } + } else { + Parser::fill_fallback_scaling_list_8x8( + scaling_lisst8x8, + i, + &DEFAULT_8X8_INTRA, + &DEFAULT_8X8_INTER, + ); + } + } + Ok(()) + } + + fn parse_pps_scaling_lists(r: &mut BitReader, pps: &mut Pps, sps: &Sps) -> Result<(), String> { + let scaling_lists4x4 = &mut pps.scaling_lists_4x4; + let scaling_lists8x8 = &mut pps.scaling_lists_8x8; + + for i in 0..6 { + let pic_scaling_list_present_flag = r.read_bit()?; + if pic_scaling_list_present_flag { + let mut use_default = false; + + Parser::parse_scaling_list(r, &mut scaling_lists4x4[i], &mut use_default)?; + + if use_default { + Parser::fill_default_scaling_list_4x4(&mut scaling_lists4x4[i], i); + } + } else if !sps.seq_scaling_matrix_present_flag { + // Table 7-2: Fallback rule A + Parser::fill_fallback_scaling_list_4x4( + scaling_lists4x4, + i, + &DEFAULT_4X4_INTRA, + &DEFAULT_4X4_INTER, + ); + } else { + // Table 7-2: Fallback rule B + Parser::fill_fallback_scaling_list_4x4( + scaling_lists4x4, + i, + &sps.scaling_lists_4x4[0], + &sps.scaling_lists_4x4[3], + ); + } + } + + if pps.transform_8x8_mode_flag { + let num8x8 = if sps.chroma_format_idc != 3 { 2 } else { 6 }; + + for i in 0..num8x8 { + let pic_scaling_list_present_flag = r.read_bit()?; + if pic_scaling_list_present_flag { + let mut use_default = false; + + Parser::parse_scaling_list(r, &mut scaling_lists8x8[i], &mut use_default)?; + + if use_default { + Parser::fill_default_scaling_list_8x8(&mut scaling_lists8x8[i], i); + } + } else if !sps.seq_scaling_matrix_present_flag { + // Table 7-2: Fallback rule A + Parser::fill_fallback_scaling_list_8x8( + scaling_lists8x8, + i, + &DEFAULT_8X8_INTRA, + &DEFAULT_8X8_INTER, + ); + } else { + // Table 7-2: Fallback rule B + Parser::fill_fallback_scaling_list_8x8( + scaling_lists8x8, + i, + &sps.scaling_lists_8x8[0], + &sps.scaling_lists_8x8[1], + ); + } + } + } + + Ok(()) + } + + fn parse_hrd(r: &mut BitReader, hrd: &mut HrdParams) -> Result<(), String> { + hrd.cpb_cnt_minus1 = r.read_ue_max(31)?; + hrd.bit_rate_scale = r.read_bits(4)?; + hrd.cpb_size_scale = r.read_bits(4)?; + + for sched_sel_idx in 0..=usize::from(hrd.cpb_cnt_minus1) { + hrd.bit_rate_value_minus1[sched_sel_idx] = r.read_ue()?; + hrd.cpb_size_value_minus1[sched_sel_idx] = r.read_ue()?; + hrd.cbr_flag[sched_sel_idx] = r.read_bit()?; + } + + hrd.initial_cpb_removal_delay_length_minus1 = r.read_bits(5)?; + hrd.cpb_removal_delay_length_minus1 = r.read_bits(5)?; + hrd.dpb_output_delay_length_minus1 = r.read_bits(5)?; + hrd.time_offset_length = r.read_bits(5)?; + Ok(()) + } + + fn parse_vui(r: &mut BitReader, sps: &mut Sps) -> Result<(), String> { + let vui = &mut sps.vui_parameters; + + vui.aspect_ratio_info_present_flag = r.read_bit()?; + if vui.aspect_ratio_info_present_flag { + vui.aspect_ratio_idc = r.read_bits(8)?; + if vui.aspect_ratio_idc == 255 { + vui.sar_width = r.read_bits(16)?; + vui.sar_height = r.read_bits(16)?; + } + } + + vui.overscan_info_present_flag = r.read_bit()?; + if vui.overscan_info_present_flag { + vui.overscan_appropriate_flag = r.read_bit()?; + } + + vui.video_signal_type_present_flag = r.read_bit()?; + if vui.video_signal_type_present_flag { + vui.video_format = r.read_bits(3)?; + vui.video_full_range_flag = r.read_bit()?; + vui.colour_description_present_flag = r.read_bit()?; + if vui.colour_description_present_flag { + vui.colour_primaries = r.read_bits(8)?; + vui.transfer_characteristics = r.read_bits(8)?; + vui.matrix_coefficients = r.read_bits(8)?; + } + } + + vui.chroma_loc_info_present_flag = r.read_bit()?; + if vui.chroma_loc_info_present_flag { + vui.chroma_sample_loc_type_top_field = r.read_ue_max(5)?; + vui.chroma_sample_loc_type_bottom_field = r.read_ue_max(5)?; + } + + vui.timing_info_present_flag = r.read_bit()?; + if vui.timing_info_present_flag { + vui.num_units_in_tick = r.read_bits::(31)? << 1; + vui.num_units_in_tick |= r.read_bit()? as u32; + if vui.num_units_in_tick == 0 { + return Err("num_units_in_tick == 0, which is not allowed by E.2.1".into()); + } + + vui.time_scale = r.read_bits::(31)? << 1; + vui.time_scale |= r.read_bit()? as u32; + if vui.time_scale == 0 { + return Err("time_scale == 0, which is not allowed by E.2.1".into()); + } + + vui.fixed_frame_rate_flag = r.read_bit()?; + } + + vui.nal_hrd_parameters_present_flag = r.read_bit()?; + if vui.nal_hrd_parameters_present_flag { + Parser::parse_hrd(r, &mut vui.nal_hrd_parameters)?; + } + + vui.vcl_hrd_parameters_present_flag = r.read_bit()?; + if vui.vcl_hrd_parameters_present_flag { + Parser::parse_hrd(r, &mut vui.vcl_hrd_parameters)?; + } + + if vui.nal_hrd_parameters_present_flag || vui.vcl_hrd_parameters_present_flag { + vui.low_delay_hrd_flag = r.read_bit()?; + } + + vui.pic_struct_present_flag = r.read_bit()?; + vui.bitstream_restriction_flag = r.read_bit()?; + + if vui.bitstream_restriction_flag { + vui.motion_vectors_over_pic_boundaries_flag = r.read_bit()?; + vui.max_bytes_per_pic_denom = r.read_ue()?; + vui.max_bits_per_mb_denom = r.read_ue_max(16)?; + vui.log2_max_mv_length_horizontal = r.read_ue_max(16)?; + vui.log2_max_mv_length_vertical = r.read_ue_max(16)?; + vui.max_num_reorder_frames = r.read_ue()?; + vui.max_dec_frame_buffering = r.read_ue()?; + } + + Ok(()) + } + + /// Parse a SPS and add it to the list of active SPSes. + /// + /// Returns a reference to the new SPS. + pub fn parse_sps(&mut self, nalu: &Nalu) -> Result<&Rc, String> { + if !matches!(nalu.header.type_, NaluType::Sps) { + return Err(format!( + "Invalid NALU type, expected {:?}, got {:?}", + NaluType::Sps, + nalu.header.type_ + )); + } + + let data = nalu.as_ref(); + // Skip the header + let mut r = BitReader::new(&data[nalu.header.len()..], true); + let mut sps = Sps { + profile_idc: r.read_bits(8)?, + constraint_set0_flag: r.read_bit()?, + constraint_set1_flag: r.read_bit()?, + constraint_set2_flag: r.read_bit()?, + constraint_set3_flag: r.read_bit()?, + constraint_set4_flag: r.read_bit()?, + constraint_set5_flag: r.read_bit()?, + ..Default::default() + }; + + // skip reserved_zero_2bits + r.skip_bits(2)?; + + let level: u8 = r.read_bits(8)?; + sps.level_idc = Level::try_from(level)?; + sps.seq_parameter_set_id = r.read_ue_max(31)?; + + if sps.profile_idc == 100 + || sps.profile_idc == 110 + || sps.profile_idc == 122 + || sps.profile_idc == 244 + || sps.profile_idc == 44 + || sps.profile_idc == 83 + || sps.profile_idc == 86 + || sps.profile_idc == 118 + || sps.profile_idc == 128 + || sps.profile_idc == 138 + || sps.profile_idc == 139 + || sps.profile_idc == 134 + || sps.profile_idc == 135 + { + sps.chroma_format_idc = r.read_ue_max(3)?; + if sps.chroma_format_idc == 3 { + sps.separate_colour_plane_flag = r.read_bit()?; + } + + sps.bit_depth_luma_minus8 = r.read_ue_max(6)?; + sps.bit_depth_chroma_minus8 = r.read_ue_max(6)?; + sps.qpprime_y_zero_transform_bypass_flag = r.read_bit()?; + sps.seq_scaling_matrix_present_flag = r.read_bit()?; + + if sps.seq_scaling_matrix_present_flag { + Parser::parse_sps_scaling_lists(&mut r, &mut sps)?; + } else { + Parser::fill_scaling_list_flat( + &mut sps.scaling_lists_4x4, + &mut sps.scaling_lists_8x8, + ); + } + } else { + sps.chroma_format_idc = 1; + Parser::fill_scaling_list_flat(&mut sps.scaling_lists_4x4, &mut sps.scaling_lists_8x8); + } + + sps.log2_max_frame_num_minus4 = r.read_ue_max(12)?; + + sps.pic_order_cnt_type = r.read_ue_max(2)?; + + if sps.pic_order_cnt_type == 0 { + sps.log2_max_pic_order_cnt_lsb_minus4 = r.read_ue_max(12)?; + sps.expected_delta_per_pic_order_cnt_cycle = 0; + } else if sps.pic_order_cnt_type == 1 { + sps.delta_pic_order_always_zero_flag = r.read_bit()?; + sps.offset_for_non_ref_pic = r.read_se()?; + sps.offset_for_top_to_bottom_field = r.read_se()?; + sps.num_ref_frames_in_pic_order_cnt_cycle = r.read_ue_max(254)?; + + let mut offset_acc = 0; + for i in 0..usize::from(sps.num_ref_frames_in_pic_order_cnt_cycle) { + sps.offset_for_ref_frame[i] = r.read_se()?; + + // (7-12) in the spec. + offset_acc += sps.offset_for_ref_frame[i]; + } + + sps.expected_delta_per_pic_order_cnt_cycle = offset_acc; + } + + sps.max_num_ref_frames = r.read_ue_max(DPB_MAX_SIZE as u32)?; + sps.gaps_in_frame_num_value_allowed_flag = r.read_bit()?; + sps.pic_width_in_mbs_minus1 = r.read_ue()?; + sps.pic_height_in_map_units_minus1 = r.read_ue()?; + sps.frame_mbs_only_flag = r.read_bit()?; + + if !sps.frame_mbs_only_flag { + sps.mb_adaptive_frame_field_flag = r.read_bit()?; + } + + sps.direct_8x8_inference_flag = r.read_bit()?; + sps.frame_cropping_flag = r.read_bit()?; + + if sps.frame_cropping_flag { + sps.frame_crop_left_offset = r.read_ue()?; + sps.frame_crop_right_offset = r.read_ue()?; + sps.frame_crop_top_offset = r.read_ue()?; + sps.frame_crop_bottom_offset = r.read_ue()?; + + // Validate that cropping info is valid. + let (crop_unit_x, crop_unit_y) = sps.crop_unit_x_y(); + + let _ = sps + .frame_crop_left_offset + .checked_add(sps.frame_crop_right_offset) + .and_then(|r| r.checked_mul(crop_unit_x)) + .and_then(|r| sps.width().checked_sub(r)) + .ok_or::("Invalid frame crop width".into())?; + + let _ = sps + .frame_crop_top_offset + .checked_add(sps.frame_crop_bottom_offset) + .and_then(|r| r.checked_mul(crop_unit_y)) + .and_then(|r| sps.height().checked_sub(r)) + .ok_or::("invalid frame crop height".into())?; + } + + sps.vui_parameters_present_flag = r.read_bit()?; + if sps.vui_parameters_present_flag { + Parser::parse_vui(&mut r, &mut sps)?; + } + + let key = sps.seq_parameter_set_id; + + if self.active_spses.keys().len() >= MAX_SPS_COUNT as usize { + return Err("Broken data: Number of active SPSs > MAX_SPS_COUNT".into()); + } + + let sps = Rc::new(sps); + self.active_spses.remove(&key); + Ok(self.active_spses.entry(key).or_insert(sps)) + } + + pub fn parse_pps(&mut self, nalu: &Nalu) -> Result<&Pps, String> { + if !matches!(nalu.header.type_, NaluType::Pps) { + return Err(format!( + "Invalid NALU type, expected {:?}, got {:?}", + NaluType::Pps, + nalu.header.type_ + )); + } + + let data = nalu.as_ref(); + // Skip the header + let mut r = BitReader::new(&data[nalu.header.len()..], true); + let pic_parameter_set_id = r.read_ue_max(MAX_PPS_COUNT as u32 - 1)?; + let seq_parameter_set_id = r.read_ue_max(MAX_SPS_COUNT as u32 - 1)?; + let sps = self.get_sps(seq_parameter_set_id).ok_or::(format!( + "Could not get SPS for seq_parameter_set_id {}", + seq_parameter_set_id + ))?; + let mut pps = Pps { + pic_parameter_set_id, + seq_parameter_set_id, + sps: Rc::clone(sps), + scaling_lists_4x4: [[0; 16]; 6], + scaling_lists_8x8: [[0; 64]; 6], + entropy_coding_mode_flag: Default::default(), + bottom_field_pic_order_in_frame_present_flag: Default::default(), + num_slice_groups_minus1: Default::default(), + num_ref_idx_l0_default_active_minus1: Default::default(), + num_ref_idx_l1_default_active_minus1: Default::default(), + weighted_pred_flag: Default::default(), + weighted_bipred_idc: Default::default(), + pic_init_qp_minus26: Default::default(), + pic_init_qs_minus26: Default::default(), + chroma_qp_index_offset: Default::default(), + deblocking_filter_control_present_flag: Default::default(), + constrained_intra_pred_flag: Default::default(), + redundant_pic_cnt_present_flag: Default::default(), + transform_8x8_mode_flag: Default::default(), + second_chroma_qp_index_offset: Default::default(), + pic_scaling_matrix_present_flag: Default::default(), + }; + + pps.entropy_coding_mode_flag = r.read_bit()?; + pps.bottom_field_pic_order_in_frame_present_flag = r.read_bit()?; + pps.num_slice_groups_minus1 = r.read_ue_max(7)?; + + if pps.num_slice_groups_minus1 > 0 { + return Err("Stream contain unsupported/unimplemented NALs".into()); + } + + pps.num_ref_idx_l0_default_active_minus1 = r.read_ue_max(31)?; + pps.num_ref_idx_l1_default_active_minus1 = r.read_ue_max(31)?; + + pps.weighted_pred_flag = r.read_bit()?; + pps.weighted_bipred_idc = r.read_bits(2)?; + + let qp_bd_offset_y = i32::from(6 * (sps.bit_depth_luma_minus8)); + pps.pic_init_qp_minus26 = r.read_se_bounded(-(26 + qp_bd_offset_y), 25)?; + pps.pic_init_qs_minus26 = r.read_se_bounded(-26, 25)?; + + pps.chroma_qp_index_offset = r.read_se_bounded(-12, 12)?; + + // When second_chroma_qp_index_offset is not present, it shall be + // inferred to be equal to chroma_qp_index_offset. + pps.second_chroma_qp_index_offset = pps.chroma_qp_index_offset; + + pps.deblocking_filter_control_present_flag = r.read_bit()?; + pps.constrained_intra_pred_flag = r.read_bit()?; + pps.redundant_pic_cnt_present_flag = r.read_bit()?; + + if r.has_more_rsbp_data() { + pps.transform_8x8_mode_flag = r.read_bit()?; + pps.pic_scaling_matrix_present_flag = r.read_bit()?; + + if pps.pic_scaling_matrix_present_flag { + Parser::parse_pps_scaling_lists(&mut r, &mut pps, sps)?; + } + + pps.second_chroma_qp_index_offset = r.read_se()?; + } + + if !pps.pic_scaling_matrix_present_flag { + // If not set, specifies that the scaling lists used for the picture + // shall be inferred to be equal to those specified by the sequence + // parameter set. When pic_scaling_matrix_present_flag is not + // present, it shall be inferred to be not set. + pps.scaling_lists_4x4 = sps.scaling_lists_4x4; + pps.scaling_lists_8x8 = sps.scaling_lists_8x8; + } + + let key = pps.pic_parameter_set_id; + + if self.active_ppses.keys().len() >= MAX_PPS_COUNT as usize { + return Err("Broken Data: number of active PPSs > MAX_PPS_COUNT".into()); + } + + let pps = Rc::new(pps); + self.active_ppses.remove(&key); + Ok(self.active_ppses.entry(key).or_insert(pps)) + } + + fn parse_ref_pic_list_modification( + r: &mut BitReader, + num_ref_idx_active_minus1: u8, + ref_list_mods: &mut Vec, + ) -> Result<(), String> { + if num_ref_idx_active_minus1 >= 32 { + return Err("Broken Data: num_ref_idx_active_minus1 >= 32".into()); + } + + loop { + let mut pic_num_mod = RefPicListModification { + modification_of_pic_nums_idc: r.read_ue_max(3)?, + ..Default::default() + }; + + match pic_num_mod.modification_of_pic_nums_idc { + 0 | 1 => { + pic_num_mod.abs_diff_pic_num_minus1 = r.read_ue()?; + } + + 2 => { + pic_num_mod.long_term_pic_num = r.read_ue()?; + } + + 3 => { + ref_list_mods.push(pic_num_mod); + break; + } + + _ => return Err("Broken Data: modification_of_pic_nums_idc > 3".into()), + } + + ref_list_mods.push(pic_num_mod); + } + + Ok(()) + } + + fn parse_ref_pic_list_modifications( + r: &mut BitReader, + header: &mut SliceHeader, + ) -> Result<(), String> { + if !header.slice_type.is_i() && !header.slice_type.is_si() { + header.ref_pic_list_modification_flag_l0 = r.read_bit()?; + if header.ref_pic_list_modification_flag_l0 { + Parser::parse_ref_pic_list_modification( + r, + header.num_ref_idx_l0_active_minus1, + &mut header.ref_pic_list_modification_l0, + )?; + } + } + + if header.slice_type.is_b() { + header.ref_pic_list_modification_flag_l1 = r.read_bit()?; + if header.ref_pic_list_modification_flag_l1 { + Parser::parse_ref_pic_list_modification( + r, + header.num_ref_idx_l1_active_minus1, + &mut header.ref_pic_list_modification_l1, + )?; + } + } + + Ok(()) + } + + fn parse_pred_weight_table( + r: &mut BitReader, + sps: &Sps, + header: &mut SliceHeader, + ) -> Result<(), String> { + let pt = &mut header.pred_weight_table; + pt.luma_log2_weight_denom = r.read_ue_max(7)?; + + // When luma_weight_l0_flag is equal to 0, luma_weight_l0[i] shall be + // inferred to be equal to 2 ^ luma_log2_weight_denom for + // RefPicList0[i]. + let default_luma_weight = 1 << pt.luma_log2_weight_denom; + for i in 0..=header.num_ref_idx_l0_active_minus1 { + pt.luma_weight_l0[usize::from(i)] = default_luma_weight; + } + + // When luma_weight_l1_flag is equal to 1, luma_weight_l1[i] shall be + // inferred to be equal to 2 ^ luma_log2_weight_denom for + // RefPicList1[i]. + if header.slice_type.is_b() { + for i in 0..=header.num_ref_idx_l1_active_minus1 { + pt.luma_weight_l1[usize::from(i)] = default_luma_weight; + } + } + + if sps.chroma_array_type() != 0 { + pt.chroma_log2_weight_denom = r.read_ue_max(7)?; + let default_chroma_weight = 1 << pt.chroma_log2_weight_denom; + + // When chroma_weight_l0_flag is equal to 0, chroma_weight_l0[i] + // shall be inferred to be equal to 2 ^ chroma_log2_weight_denom for + // RefPicList0[i]. + for i in 0..=header.num_ref_idx_l0_active_minus1 { + pt.chroma_weight_l0[usize::from(i)][0] = default_chroma_weight; + pt.chroma_weight_l0[usize::from(i)][1] = default_chroma_weight; + } + + // When chroma_weight_l1_flag is equal to 0, chroma_weight_l1[i] + // shall be inferred to be equal to 2 ^ chroma_log2_weight_denom for + // RefPicList1[i]. + for i in 0..=header.num_ref_idx_l1_active_minus1 { + pt.chroma_weight_l1[usize::from(i)][0] = default_chroma_weight; + pt.chroma_weight_l1[usize::from(i)][1] = default_chroma_weight; + } + } + + for i in 0..=header.num_ref_idx_l0_active_minus1 { + let luma_weight_l0_flag = r.read_bit()?; + + if luma_weight_l0_flag { + pt.luma_weight_l0[usize::from(i)] = r.read_se_bounded(-128, 127)?; + pt.luma_offset_l0[usize::from(i)] = r.read_se_bounded(-128, 127)?; + } + + if sps.chroma_array_type() != 0 { + let chroma_weight_l0_flag = r.read_bit()?; + if chroma_weight_l0_flag { + for j in 0..2 { + pt.chroma_weight_l0[usize::from(i)][j] = r.read_se_bounded(-128, 127)?; + pt.chroma_offset_l0[usize::from(i)][j] = r.read_se_bounded(-128, 127)?; + } + } + } + } + + if header.slice_type.is_b() { + for i in 0..=header.num_ref_idx_l1_active_minus1 { + let luma_weight_l1_flag = r.read_bit()?; + + if luma_weight_l1_flag { + pt.luma_weight_l1[usize::from(i)] = r.read_se_bounded(-128, 127)?; + pt.luma_offset_l1[usize::from(i)] = r.read_se_bounded(-128, 127)?; + } + + if sps.chroma_array_type() != 0 { + let chroma_weight_l1_flag = r.read_bit()?; + if chroma_weight_l1_flag { + for j in 0..2 { + pt.chroma_weight_l1[usize::from(i)][j] = + r.read_se_bounded(-128, 127)?; + pt.chroma_offset_l1[usize::from(i)][j] = + r.read_se_bounded(-128, 127)?; + } + } + } + } + } + + Ok(()) + } + + fn parse_dec_ref_pic_marking( + r: &mut BitReader, + nalu: &Nalu, + header: &mut SliceHeader, + ) -> Result<(), String> { + let rpm = &mut header.dec_ref_pic_marking; + + let num_bits_left = r.num_bits_left(); + if nalu.header.idr_pic_flag { + rpm.no_output_of_prior_pics_flag = r.read_bit()?; + rpm.long_term_reference_flag = r.read_bit()?; + } else { + rpm.adaptive_ref_pic_marking_mode_flag = r.read_bit()?; + + if rpm.adaptive_ref_pic_marking_mode_flag { + loop { + let mut marking = RefPicMarkingInner::default(); + + let mem_mgmt_ctrl_op = r.read_ue_max::(6)?; + marking.memory_management_control_operation = mem_mgmt_ctrl_op; + + if mem_mgmt_ctrl_op == 0 { + break; + } + + if mem_mgmt_ctrl_op == 1 || mem_mgmt_ctrl_op == 3 { + marking.difference_of_pic_nums_minus1 = r.read_ue()?; + } + + if mem_mgmt_ctrl_op == 2 { + marking.long_term_pic_num = r.read_ue()?; + } + + if mem_mgmt_ctrl_op == 3 || mem_mgmt_ctrl_op == 6 { + marking.long_term_frame_idx = r.read_ue()?; + } + + if mem_mgmt_ctrl_op == 4 { + marking.max_long_term_frame_idx = + MaxLongTermFrameIdx::from_value_plus1(r.read_ue()?); + } + + rpm.inner.push(marking); + } + } + } + header.dec_ref_pic_marking_bit_size = num_bits_left - r.num_bits_left(); + + Ok(()) + } + + pub fn parse_slice_header<'a>(&self, nalu: Nalu<'a>) -> Result, String> { + if !matches!( + nalu.header.type_, + NaluType::Slice + | NaluType::SliceDpa + | NaluType::SliceDpb + | NaluType::SliceDpc + | NaluType::SliceIdr + | NaluType::SliceExt + ) { + return Err(format!("Invalid NALU type: {:?} is not a slice NALU", nalu.header.type_)); + } + + let data = nalu.as_ref(); + // Skip the header + let mut r = BitReader::new(&data[nalu.header.len()..], true); + + let mut header = SliceHeader { first_mb_in_slice: r.read_ue()?, ..Default::default() }; + + let slice_type = r.read_ue_max::(9)? % 5; + header.slice_type = SliceType::try_from(slice_type)?; + + header.pic_parameter_set_id = r.read_ue()?; + + let pps = self.get_pps(header.pic_parameter_set_id).ok_or::(format!( + "Could not get PPS for pic_parameter_set_id {}", + header.pic_parameter_set_id + ))?; + + let sps = &pps.sps; + + if sps.separate_colour_plane_flag { + header.colour_plane_id = r.read_bits(2)?; + } + + header.frame_num = r.read_bits(usize::from(sps.log2_max_frame_num_minus4) + 4)?; + + if !sps.frame_mbs_only_flag { + header.field_pic_flag = r.read_bit()?; + if header.field_pic_flag { + header.bottom_field_flag = r.read_bit()?; + } + } + + if header.field_pic_flag { + header.max_pic_num = 2 * sps.max_frame_num(); + } else { + header.max_pic_num = sps.max_frame_num(); + } + + if nalu.header.idr_pic_flag { + header.idr_pic_id = r.read_ue_max(0xffff)?; + } + + let num_bits_left = r.num_bits_left(); + if sps.pic_order_cnt_type == 0 { + header.pic_order_cnt_lsb = + r.read_bits(usize::from(sps.log2_max_pic_order_cnt_lsb_minus4) + 4)?; + + if pps.bottom_field_pic_order_in_frame_present_flag && !header.field_pic_flag { + header.delta_pic_order_cnt_bottom = r.read_se()?; + } + } + + if sps.pic_order_cnt_type == 1 && !sps.delta_pic_order_always_zero_flag { + header.delta_pic_order_cnt[0] = r.read_se()?; + if pps.bottom_field_pic_order_in_frame_present_flag && !header.field_pic_flag { + header.delta_pic_order_cnt[1] = r.read_se()?; + } + } + header.pic_order_cnt_bit_size = num_bits_left - r.num_bits_left(); + + if pps.redundant_pic_cnt_present_flag { + header.redundant_pic_cnt = r.read_ue_max(127)?; + } + + if header.slice_type.is_b() { + header.direct_spatial_mv_pred_flag = r.read_bit()?; + } + + if header.slice_type.is_p() || header.slice_type.is_sp() || header.slice_type.is_b() { + header.num_ref_idx_active_override_flag = r.read_bit()?; + if header.num_ref_idx_active_override_flag { + header.num_ref_idx_l0_active_minus1 = r.read_ue()?; + if header.slice_type.is_b() { + header.num_ref_idx_l1_active_minus1 = r.read_ue()?; + } + } else { + header.num_ref_idx_l0_active_minus1 = pps.num_ref_idx_l0_default_active_minus1; + if header.slice_type.is_b() { + header.num_ref_idx_l1_active_minus1 = pps.num_ref_idx_l1_default_active_minus1; + } + } + } + + if header.field_pic_flag { + if header.num_ref_idx_l0_active_minus1 > 31 || header.num_ref_idx_l1_active_minus1 > 31 + { + return Err("Broken Data".into()); + } + } else if header.num_ref_idx_l0_active_minus1 > 15 + || header.num_ref_idx_l1_active_minus1 > 15 + { + return Err("Broken Data".into()); + } + + if let NaluType::SliceExt = nalu.header.type_ { + return Err("Stream contain unsupported/unimplemented NALs".into()); + } + + Parser::parse_ref_pic_list_modifications(&mut r, &mut header)?; + + if (pps.weighted_pred_flag && (header.slice_type.is_p() || header.slice_type.is_sp())) + || (pps.weighted_bipred_idc == 1 && header.slice_type.is_b()) + { + Parser::parse_pred_weight_table(&mut r, sps, &mut header)?; + } + + if nalu.header.ref_idc != 0 { + Parser::parse_dec_ref_pic_marking(&mut r, &nalu, &mut header)?; + } + + if pps.entropy_coding_mode_flag && !header.slice_type.is_i() && !header.slice_type.is_si() { + header.cabac_init_idc = r.read_ue_max(2)?; + } + + header.slice_qp_delta = r.read_se_bounded(-87, 77)?; + + if header.slice_type.is_sp() || header.slice_type.is_si() { + if header.slice_type.is_sp() { + header.sp_for_switch_flag = r.read_bit()?; + } + + header.slice_qs_delta = r.read_se_bounded(-51, 51)?; + } + + if pps.deblocking_filter_control_present_flag { + header.disable_deblocking_filter_idc = r.read_ue_max(2)?; + + if header.disable_deblocking_filter_idc != 1 { + header.slice_alpha_c0_offset_div2 = r.read_se_bounded(-6, 6)?; + header.slice_beta_offset_div2 = r.read_se_bounded(-6, 6)?; + } + } + + if pps.num_slice_groups_minus1 > 0 { + return Err("Stream contain unsupported/unimplemented NALs".into()); + } + + let epb = r.num_epb(); + header.header_bit_size = (nalu.size - epb) * 8 - r.num_bits_left(); + + header.n_emulation_prevention_bytes = epb; + + Ok(Slice { header, nalu }) + } + + pub fn get_sps(&self, sps_id: u8) -> Option<&Rc> { + self.active_spses.get(&sps_id) + } + + pub fn get_pps(&self, pps_id: u8) -> Option<&Rc> { + self.active_ppses.get(&pps_id) + } +} + +#[derive(Debug)] +pub struct NaluHeader { + pub ref_idc: u8, + pub type_: NaluType, + pub idr_pic_flag: bool, +} + +impl Header for NaluHeader { + fn parse>(cursor: &mut Cursor) -> Result { + let mut byte_buf = [0u8; 1]; + cursor.read_exact(&mut byte_buf).map_err(|_| String::from("Broken Data"))?; + let byte = byte_buf[0]; + let _ = cursor.seek(SeekFrom::Current(-1 * byte_buf.len() as i64)); + + let type_ = NaluType::try_from(byte & 0x1f)?; + + if let NaluType::SliceExt = type_ { + return Err("Stream contain unsupported/unimplemented NALs".into()); + } + + let ref_idc = (byte & 0x60) >> 5; + let idr_pic_flag = matches!(type_, NaluType::SliceIdr); + + Ok(NaluHeader { ref_idc, type_, idr_pic_flag }) + } + + fn is_end(&self) -> bool { + matches!(self.type_, NaluType::SeqEnd | NaluType::StreamEnd) + } + + fn len(&self) -> usize { + 1 + } +} + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + use crate::codec::h264::parser::Level; + use crate::codec::h264::parser::MaxLongTermFrameIdx; + use crate::codec::h264::parser::Nalu; + use crate::codec::h264::parser::NaluType; + use crate::codec::h264::parser::Parser; + + const STREAM_TEST_25_FPS: &[u8] = include_bytes!("test_data/test-25fps.h264"); + const STREAM_TEST_25_FPS_NUM_NALUS: usize = 759; + + const STREAM_TEST_25_FPS_SLICE_0: &[u8] = + include_bytes!("test_data/test-25fps-h264-slice-data-0.bin"); + const STREAM_TEST_25_FPS_SLICE_2: &[u8] = + include_bytes!("test_data/test-25fps-h264-slice-data-2.bin"); + const STREAM_TEST_25_FPS_SLICE_4: &[u8] = + include_bytes!("test_data/test-25fps-h264-slice-data-4.bin"); + + /// This test is adapted from chromium, available at media/video/h264_parser_unittest.cc + #[test] + fn parse_nalus_from_stream_file() { + let mut cursor = Cursor::new(STREAM_TEST_25_FPS); + let mut num_nalus = 0; + while Nalu::next(&mut cursor).is_ok() { + num_nalus += 1; + } + + assert_eq!(num_nalus, STREAM_TEST_25_FPS_NUM_NALUS) + } + + /// The results were manually extracted from the GStreamer parser using GDB + /// (gsth264parser.c) in order to compare both implementations using the + /// following pipeline: + /// gst-launch-1.0 filesrc location=test-25fps.h264 ! h264parse ! 'video/x-h264,stream-format=byte-stream' ! vah264dec ! fakevideosink + #[test] + fn parse_test25fps() { + let mut cursor = Cursor::new(STREAM_TEST_25_FPS); + let mut sps_ids = Vec::new(); + let mut pps_ids = Vec::new(); + let mut slices = Vec::new(); + + let mut parser = Parser::default(); + + while let Ok(nalu) = Nalu::next(&mut cursor) { + match nalu.header.type_ { + NaluType::Slice + | NaluType::SliceDpa + | NaluType::SliceDpb + | NaluType::SliceDpc + | NaluType::SliceIdr + | NaluType::SliceExt => { + let slice = parser.parse_slice_header(nalu).unwrap(); + slices.push(slice); + } + NaluType::Sps => { + let sps = parser.parse_sps(&nalu).unwrap(); + sps_ids.push(sps.seq_parameter_set_id); + } + NaluType::Pps => { + let pps = parser.parse_pps(&nalu).unwrap(); + pps_ids.push(pps.pic_parameter_set_id); + } + _ => { + continue; + } + } + } + + for sps_id in &sps_ids { + // four identical SPSes in this stream + let sps = parser.get_sps(*sps_id).unwrap(); + + assert_eq!(sps.seq_parameter_set_id, 0); + assert_eq!(sps.profile_idc, 77); + assert!(!sps.constraint_set0_flag); + assert!(sps.constraint_set1_flag); + assert!(!sps.constraint_set2_flag); + assert!(!sps.constraint_set3_flag); + assert!(!sps.constraint_set4_flag); + assert!(!sps.constraint_set5_flag); + assert_eq!(sps.level_idc, Level::L1_3); + assert_eq!(sps.chroma_format_idc, 1); + assert!(!sps.separate_colour_plane_flag); + assert_eq!(sps.bit_depth_luma_minus8, 0); + assert_eq!(sps.bit_depth_chroma_minus8, 0); + assert!(!sps.qpprime_y_zero_transform_bypass_flag); + assert!(!sps.seq_scaling_matrix_present_flag); + + for outer in &sps.scaling_lists_4x4 { + for inner in outer { + assert_eq!(*inner, 16); + } + } + + for outer in &sps.scaling_lists_8x8 { + for inner in outer { + assert_eq!(*inner, 16); + } + } + + assert_eq!(sps.log2_max_frame_num_minus4, 1); + assert_eq!(sps.pic_order_cnt_type, 0); + assert_eq!(sps.log2_max_pic_order_cnt_lsb_minus4, 3); + assert!(!sps.delta_pic_order_always_zero_flag); + assert_eq!(sps.offset_for_non_ref_pic, 0); + assert_eq!(sps.offset_for_top_to_bottom_field, 0); + assert_eq!(sps.num_ref_frames_in_pic_order_cnt_cycle, 0); + + for offset in sps.offset_for_ref_frame { + assert_eq!(offset, 0); + } + + assert_eq!(sps.max_num_ref_frames, 2); + assert!(!sps.gaps_in_frame_num_value_allowed_flag); + assert_eq!(sps.pic_width_in_mbs_minus1, 19); + assert_eq!(sps.pic_height_in_map_units_minus1, 14); + assert!(sps.frame_mbs_only_flag); + assert!(!sps.mb_adaptive_frame_field_flag); + assert!(!sps.direct_8x8_inference_flag); + assert!(!sps.frame_cropping_flag); + assert_eq!(sps.frame_crop_left_offset, 0); + assert_eq!(sps.frame_crop_right_offset, 0); + assert_eq!(sps.frame_crop_top_offset, 0); + assert_eq!(sps.frame_crop_bottom_offset, 0); + assert_eq!(sps.chroma_array_type(), 1); + assert_eq!(sps.max_frame_num(), 32); + assert_eq!(sps.width(), 320); + assert_eq!(sps.height(), 240); + } + + for pps_id in &pps_ids { + // four identical SPSes in this stream + let pps = parser.get_pps(*pps_id).unwrap(); + assert_eq!(pps.pic_parameter_set_id, 0); + assert_eq!(pps.seq_parameter_set_id, 0); + assert!(pps.bottom_field_pic_order_in_frame_present_flag); + assert_eq!(pps.num_slice_groups_minus1, 0); + assert_eq!(pps.num_ref_idx_l0_default_active_minus1, 0); + assert_eq!(pps.num_ref_idx_l1_default_active_minus1, 0); + assert!(!pps.weighted_pred_flag); + assert_eq!(pps.weighted_bipred_idc, 0); + assert_eq!(pps.pic_init_qp_minus26, 2); + assert_eq!(pps.pic_init_qs_minus26, 0); + assert_eq!(pps.chroma_qp_index_offset, 0); + assert!(!pps.deblocking_filter_control_present_flag); + assert!(!pps.constrained_intra_pred_flag); + assert!(!pps.redundant_pic_cnt_present_flag); + assert!(!pps.transform_8x8_mode_flag); + + for outer in &pps.scaling_lists_4x4 { + for inner in outer { + assert_eq!(*inner, 16); + } + } + + for outer in &pps.scaling_lists_8x8 { + for inner in outer { + assert_eq!(*inner, 16); + } + } + + assert_eq!(pps.second_chroma_qp_index_offset, 0); + assert!(!pps.pic_scaling_matrix_present_flag); + } + + // test an I slice + let hdr = &slices[0].header; + let nalu = &slices[0].nalu; + assert_eq!(nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_0); + + assert_eq!(hdr.first_mb_in_slice, 0); + assert!(hdr.slice_type.is_i()); + assert_eq!(hdr.colour_plane_id, 0); + assert_eq!(hdr.frame_num, 0); + assert!(!hdr.field_pic_flag); + assert!(!hdr.bottom_field_flag); + assert_eq!(hdr.idr_pic_id, 0); + assert_eq!(hdr.pic_order_cnt_lsb, 0); + assert_eq!(hdr.delta_pic_order_cnt_bottom, 0); + assert_eq!(hdr.delta_pic_order_cnt[0], 0); + assert_eq!(hdr.delta_pic_order_cnt[1], 0); + assert_eq!(hdr.redundant_pic_cnt, 0); + assert!(!hdr.direct_spatial_mv_pred_flag); + assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); + assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); + assert!(!hdr.ref_pic_list_modification_flag_l0); + + assert_eq!(hdr.ref_pic_list_modification_l0.len(), 0); + + for rplm in &hdr.ref_pic_list_modification_l0 { + assert_eq!(rplm.modification_of_pic_nums_idc, 0); + assert_eq!(rplm.abs_diff_pic_num_minus1, 0); + assert_eq!(rplm.long_term_pic_num, 0); + assert_eq!(rplm.abs_diff_view_idx_minus1, 0); + } + + assert!(!hdr.ref_pic_list_modification_flag_l1); + assert_eq!(hdr.ref_pic_list_modification_l1.len(), 0); + + for rplm in &hdr.ref_pic_list_modification_l1 { + assert_eq!(rplm.modification_of_pic_nums_idc, 0); + assert_eq!(rplm.abs_diff_pic_num_minus1, 0); + assert_eq!(rplm.long_term_pic_num, 0); + assert_eq!(rplm.abs_diff_view_idx_minus1, 0); + } + + // Safe because this type does not have any references + assert_eq!(hdr.pred_weight_table, unsafe { std::mem::zeroed() }); + + assert_eq!(hdr.dec_ref_pic_marking, Default::default()); + + assert_eq!(hdr.cabac_init_idc, 0); + assert_eq!(hdr.slice_qp_delta, 12); + assert_eq!(hdr.slice_qs_delta, 0); + assert_eq!(hdr.disable_deblocking_filter_idc, 0); + assert_eq!(hdr.slice_alpha_c0_offset_div2, 0); + assert_eq!(hdr.slice_beta_offset_div2, 0); + assert_eq!(hdr.max_pic_num, 32); + assert_eq!(hdr.header_bit_size, 38); + assert!(!hdr.num_ref_idx_active_override_flag); + + // test a P slice + let hdr = &slices[2].header; + let nalu = &slices[2].nalu; + assert_eq!(nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_2); + + assert_eq!(hdr.first_mb_in_slice, 0); + assert!(hdr.slice_type.is_p()); + assert_eq!(hdr.colour_plane_id, 0); + assert_eq!(hdr.frame_num, 1); + assert!(!hdr.field_pic_flag); + assert!(!hdr.bottom_field_flag); + assert_eq!(hdr.idr_pic_id, 0); + assert_eq!(hdr.pic_order_cnt_lsb, 4); + assert_eq!(hdr.delta_pic_order_cnt_bottom, 0); + assert_eq!(hdr.delta_pic_order_cnt[0], 0); + assert_eq!(hdr.delta_pic_order_cnt[1], 0); + assert_eq!(hdr.redundant_pic_cnt, 0); + assert!(!hdr.direct_spatial_mv_pred_flag); + assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); + assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); + assert!(!hdr.ref_pic_list_modification_flag_l0); + + assert_eq!(hdr.ref_pic_list_modification_l0.len(), 0); + + for rplm in &hdr.ref_pic_list_modification_l0 { + assert_eq!(rplm.modification_of_pic_nums_idc, 0); + assert_eq!(rplm.abs_diff_pic_num_minus1, 0); + assert_eq!(rplm.long_term_pic_num, 0); + assert_eq!(rplm.abs_diff_view_idx_minus1, 0); + } + + assert!(!hdr.ref_pic_list_modification_flag_l1); + assert_eq!(hdr.ref_pic_list_modification_l1.len(), 0); + + for rplm in &hdr.ref_pic_list_modification_l1 { + assert_eq!(rplm.modification_of_pic_nums_idc, 0); + assert_eq!(rplm.abs_diff_pic_num_minus1, 0); + assert_eq!(rplm.long_term_pic_num, 0); + assert_eq!(rplm.abs_diff_view_idx_minus1, 0); + } + + // Safe because this type does not have any references + assert_eq!(hdr.pred_weight_table, unsafe { std::mem::zeroed() }); + + assert_eq!(hdr.dec_ref_pic_marking, Default::default()); + + assert_eq!(hdr.cabac_init_idc, 0); + assert_eq!(hdr.slice_qp_delta, 0); + assert_eq!(hdr.slice_qs_delta, 0); + assert_eq!(hdr.disable_deblocking_filter_idc, 0); + assert_eq!(hdr.slice_alpha_c0_offset_div2, 0); + assert_eq!(hdr.slice_beta_offset_div2, 0); + assert_eq!(hdr.max_pic_num, 32); + assert_eq!(hdr.header_bit_size, 28); + assert!(!hdr.num_ref_idx_active_override_flag); + + // test a B slice + let hdr = &slices[4].header; + let nalu = &slices[4].nalu; + assert_eq!(nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_4); + + assert_eq!(hdr.first_mb_in_slice, 0); + assert!(hdr.slice_type.is_b()); + assert_eq!(hdr.colour_plane_id, 0); + assert_eq!(hdr.frame_num, 2); + assert!(!hdr.field_pic_flag); + assert!(!hdr.bottom_field_flag); + assert_eq!(hdr.idr_pic_id, 0); + assert_eq!(hdr.pic_order_cnt_lsb, 2); + assert_eq!(hdr.delta_pic_order_cnt_bottom, 0); + assert_eq!(hdr.delta_pic_order_cnt[0], 0); + assert_eq!(hdr.delta_pic_order_cnt[1], 0); + assert_eq!(hdr.redundant_pic_cnt, 0); + assert!(hdr.direct_spatial_mv_pred_flag); + assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); + assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); + assert!(!hdr.ref_pic_list_modification_flag_l0); + + assert_eq!(hdr.ref_pic_list_modification_l0.len(), 0); + + for rplm in &hdr.ref_pic_list_modification_l0 { + assert_eq!(rplm.modification_of_pic_nums_idc, 0); + assert_eq!(rplm.abs_diff_pic_num_minus1, 0); + assert_eq!(rplm.long_term_pic_num, 0); + assert_eq!(rplm.abs_diff_view_idx_minus1, 0); + } + + assert!(!hdr.ref_pic_list_modification_flag_l1); + assert_eq!(hdr.ref_pic_list_modification_l1.len(), 0); + + for rplm in &hdr.ref_pic_list_modification_l1 { + assert_eq!(rplm.modification_of_pic_nums_idc, 0); + assert_eq!(rplm.abs_diff_pic_num_minus1, 0); + assert_eq!(rplm.long_term_pic_num, 0); + assert_eq!(rplm.abs_diff_view_idx_minus1, 0); + } + + // Safe because this type does not have any references + assert_eq!(hdr.pred_weight_table, unsafe { std::mem::zeroed() }); + + assert_eq!(hdr.dec_ref_pic_marking, Default::default()); + + assert_eq!(hdr.cabac_init_idc, 0); + assert_eq!(hdr.slice_qp_delta, 16); + assert_eq!(hdr.slice_qs_delta, 0); + assert_eq!(hdr.disable_deblocking_filter_idc, 0); + assert_eq!(hdr.slice_alpha_c0_offset_div2, 0); + assert_eq!(hdr.slice_beta_offset_div2, 0); + assert_eq!(hdr.max_pic_num, 32); + assert_eq!(hdr.header_bit_size, 41); + assert!(!hdr.num_ref_idx_active_override_flag); + } + + #[test] + fn invalid_sps_crop_width() { + // This SPS contains invalid frame_crop_*_offset settings. This led to + // unconditional panic in the parser in the past. This test make sure a + // panic is avoided. + let invalid_sps = + vec![0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x0a, 0xfb, 0xb0, 0x32, 0xc0, 0xca, 0x80]; + + let mut cursor = Cursor::new(invalid_sps.as_ref()); + let mut parser = Parser::default(); + + while let Ok(nalu) = Nalu::next(&mut cursor) { + assert_eq!(nalu.header.type_, NaluType::Sps); + parser.parse_sps(&nalu).unwrap_err(); + } + } + + #[test] + fn max_long_term_frame_idx() { + assert_eq!( + MaxLongTermFrameIdx::from_value_plus1(0), + MaxLongTermFrameIdx::NoLongTermFrameIndices + ); + assert_eq!(MaxLongTermFrameIdx::NoLongTermFrameIndices.to_value_plus1(), 0); + + assert_eq!(MaxLongTermFrameIdx::from_value_plus1(1), MaxLongTermFrameIdx::Idx(0)); + assert_eq!(MaxLongTermFrameIdx::Idx(0).to_value_plus1(), 1); + + assert_eq!(MaxLongTermFrameIdx::from_value_plus1(25), MaxLongTermFrameIdx::Idx(24)); + assert_eq!(MaxLongTermFrameIdx::Idx(24).to_value_plus1(), 25); + + // Check PartialOrd implementation. + assert!(MaxLongTermFrameIdx::NoLongTermFrameIndices < 0); + assert_ne!(MaxLongTermFrameIdx::NoLongTermFrameIndices, 0); + assert_eq!(MaxLongTermFrameIdx::Idx(0), 0); + assert!(MaxLongTermFrameIdx::Idx(0) < 1); + assert_eq!(MaxLongTermFrameIdx::Idx(24), 24); + assert!(MaxLongTermFrameIdx::Idx(24) < 25); + } +} diff --git a/vendor/cros-codecs/src/codec/h264/picture.rs b/vendor/cros-codecs/src/codec/h264/picture.rs new file mode 100644 index 00000000..ed8bc75f --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/picture.rs @@ -0,0 +1,415 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::cell::RefCell; +use std::ops::Deref; +use std::rc::Rc; +use std::rc::Weak; + +use log::debug; + +use crate::codec::h264::parser::MaxLongTermFrameIdx; +use crate::codec::h264::parser::RefPicMarking; +use crate::codec::h264::parser::Slice; +use crate::codec::h264::parser::SliceType; +use crate::codec::h264::parser::Sps; +use crate::Resolution; + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum Field { + #[default] + Frame, + Top, + Bottom, +} + +impl Field { + /// Returns the field of opposite parity. + pub fn opposite(&self) -> Self { + match *self { + Field::Frame => Field::Frame, + Field::Top => Field::Bottom, + Field::Bottom => Field::Top, + } + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum Reference { + #[default] + None, + ShortTerm, + LongTerm, +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum IsIdr { + #[default] + No, + Yes { + idr_pic_id: u16, + }, +} + +/// The rank of a field, i.e. whether it is the first or second one to be parsed from the stream. +/// This is unrelated to the `Field` type, as the first field can be either `Top` or `Bottom`. +#[derive(Default, Debug)] +pub enum FieldRank { + /// Frame has a single field. + #[default] + Single, + /// Frame is interlaced, and this is the first field (with a reference to the second one). + First(Weak>), + /// Frame is interlaced, and this is the second field (with a reference to the first one). + Second(Rc>), +} + +#[derive(Default)] +pub struct PictureData { + pub pic_order_cnt_type: u8, + pub top_field_order_cnt: i32, + pub bottom_field_order_cnt: i32, + pub pic_order_cnt: i32, + pub pic_order_cnt_msb: i32, + pub pic_order_cnt_lsb: i32, + pub delta_pic_order_cnt_bottom: i32, + pub delta_pic_order_cnt0: i32, + pub delta_pic_order_cnt1: i32, + + pub pic_num: i32, + pub long_term_pic_num: u32, + pub frame_num: u32, + pub frame_num_offset: u32, + pub frame_num_wrap: i32, + pub long_term_frame_idx: u32, + + pub coded_resolution: Resolution, + pub display_resolution: Resolution, + + pub type_: SliceType, + pub nal_ref_idc: u8, + pub is_idr: IsIdr, + reference: Reference, + pub ref_pic_list_modification_flag_l0: i32, + pub abs_diff_pic_num_minus1: i32, + + // Does memory management op 5 needs to be executed after this + // picture has finished decoding? + pub has_mmco_5: bool, + + // Created by the decoding process for gaps in frame_num. + // Not for decode or output. + pub nonexisting: bool, + + pub field: Field, + + // Values from slice_hdr to be used during reference marking and + // memory management after finishing this picture. + pub ref_pic_marking: RefPicMarking, + + field_rank: FieldRank, + + pub timestamp: u64, +} + +/// A `PictureData` within a `Rc` which field rank is guaranteed to be correct. +/// +/// The field rank of `PictureData` is only final after both fields have been constructed - namely, +/// the first field can only point to the second one after the latter is available as a Rc. Methods +/// [`PictureData::into_rc`] and [`PictureData::split_frame`] take care of this, and is this only +/// producer of this type, ensuring all instances are correct. +#[derive(Default, Debug, Clone)] +pub struct RcPictureData { + pic: Rc>, +} + +impl Deref for RcPictureData { + type Target = Rc>; + + fn deref(&self) -> &Self::Target { + &self.pic + } +} + +impl PictureData { + pub fn new_non_existing(frame_num: u32, timestamp: u64) -> Self { + PictureData { + frame_num, + nonexisting: true, + nal_ref_idc: 1, + field: Field::Frame, + pic_num: frame_num as i32, + reference: Reference::ShortTerm, + timestamp, + ..Default::default() + } + } + + /// Create a new picture from a `slice`, `sps`, and `timestamp`. + /// + /// `first_field` is set if this picture is the second field of a frame. + pub fn new_from_slice( + slice: &Slice, + sps: &Sps, + timestamp: u64, + first_field: Option<&RcPictureData>, + ) -> Self { + let hdr = &slice.header; + let nalu_hdr = &slice.nalu.header; + + let is_idr = if nalu_hdr.idr_pic_flag { + IsIdr::Yes { idr_pic_id: hdr.idr_pic_id } + } else { + IsIdr::No + }; + + let field = if hdr.field_pic_flag { + if hdr.bottom_field_flag { + Field::Bottom + } else { + Field::Top + } + } else { + Field::Frame + }; + + let reference = if nalu_hdr.ref_idc != 0 { Reference::ShortTerm } else { Reference::None }; + + let pic_num = if !hdr.field_pic_flag { hdr.frame_num } else { 2 * hdr.frame_num + 1 }; + + let ( + pic_order_cnt_lsb, + delta_pic_order_cnt_bottom, + delta_pic_order_cnt0, + delta_pic_order_cnt1, + ) = match sps.pic_order_cnt_type { + 0 => ( + hdr.pic_order_cnt_lsb, + hdr.delta_pic_order_cnt_bottom, + Default::default(), + Default::default(), + ), + 1 => ( + Default::default(), + Default::default(), + hdr.delta_pic_order_cnt[0], + hdr.delta_pic_order_cnt[1], + ), + _ => (Default::default(), Default::default(), Default::default(), Default::default()), + }; + + let coded_resolution = Resolution::from((sps.width(), sps.height())); + + let visible_rect = sps.visible_rectangle(); + + let display_resolution = Resolution { + width: visible_rect.max.x - visible_rect.min.x, + height: visible_rect.max.y - visible_rect.min.y, + }; + + let mut pic = PictureData { + pic_order_cnt_type: sps.pic_order_cnt_type, + pic_order_cnt_lsb: i32::from(pic_order_cnt_lsb), + delta_pic_order_cnt_bottom, + delta_pic_order_cnt0, + delta_pic_order_cnt1, + pic_num: i32::from(pic_num), + frame_num: u32::from(hdr.frame_num), + nal_ref_idc: nalu_hdr.ref_idc, + is_idr, + reference, + field, + ref_pic_marking: hdr.dec_ref_pic_marking.clone(), + coded_resolution, + display_resolution, + timestamp, + ..Default::default() + }; + + if let Some(first_field) = first_field { + pic.set_first_field_to(first_field); + } + + pic + } + + /// Whether the current picture is a reference, either ShortTerm or LongTerm. + pub fn is_ref(&self) -> bool { + !matches!(self.reference, Reference::None) + } + + /// Whether this picture is a second field. + pub fn is_second_field(&self) -> bool { + matches!(self.field_rank, FieldRank::Second(..)) + } + + /// Returns the field rank of this picture, including a reference to its other field. + pub fn field_rank(&self) -> &FieldRank { + &self.field_rank + } + + /// Returns a reference to the picture's Reference + pub fn reference(&self) -> &Reference { + &self.reference + } + + /// Mark the picture as a reference picture. + pub fn set_reference(&mut self, reference: Reference, apply_to_other_field: bool) { + log::debug!("Set reference of {:#?} to {:?}", self, reference); + self.reference = reference; + + if apply_to_other_field { + if let Some(other_field) = self.other_field() { + log::debug!( + "other_field: Set reference of {:#?} to {:?}", + &other_field.borrow(), + reference + ); + other_field.borrow_mut().reference = reference; + } + } + } + + /// Get a reference to the picture's other field, if there is any + /// and its reference is still valid. + pub fn other_field(&self) -> Option>> { + match &self.field_rank { + FieldRank::Single => None, + FieldRank::First(other_field) => other_field.upgrade(), + FieldRank::Second(other_field) => Some(other_field.clone()), + } + } + + /// Set this picture's second field. + fn set_second_field_to(&mut self, other_field: &Rc>) { + self.field_rank = FieldRank::First(Rc::downgrade(other_field)); + } + + /// Whether the current picture is the second field of a complementary ref pair. + pub fn is_second_field_of_complementary_ref_pair(&self) -> bool { + self.is_ref() + && matches!(self.field_rank(), FieldRank::Second(first_field) if first_field.borrow().is_ref()) + } + + /// Set this picture's first field. + fn set_first_field_to(&mut self, other_field: &Rc>) { + self.field_rank = FieldRank::Second(other_field.clone()); + } + + pub fn pic_num_f(&self, max_pic_num: i32) -> i32 { + if !matches!(self.reference(), Reference::LongTerm) { + self.pic_num + } else { + max_pic_num + } + } + + pub fn long_term_pic_num_f(&self, max_long_term_frame_idx: MaxLongTermFrameIdx) -> u32 { + if matches!(self.reference(), Reference::LongTerm) { + self.long_term_pic_num + } else { + 2 * max_long_term_frame_idx.to_value_plus1() + } + } + + /// Consume this picture and return a Rc'd version. + /// + /// If the picture was a second field, adjust the field of the first field to point to this + /// one. + pub fn into_rc(self) -> RcPictureData { + let self_rc = Rc::new(RefCell::new(self)); + + if let FieldRank::Second(first_field) = self_rc.borrow().field_rank() { + first_field.borrow_mut().set_second_field_to(&self_rc); + } + + RcPictureData { pic: self_rc } + } + + /// Split a frame into two complementary fields that reference one another. + pub fn split_frame(mut self) -> (RcPictureData, RcPictureData) { + assert!(matches!(self.field, Field::Frame)); + assert!(matches!(self.field_rank, FieldRank::Single)); + + debug!( + "Splitting picture (frame_num, POC) ({:?}, {:?})", + self.frame_num, self.pic_order_cnt + ); + + let second_pic_order_cnt = if self.top_field_order_cnt < self.bottom_field_order_cnt { + self.field = Field::Top; + self.pic_order_cnt = self.top_field_order_cnt; + + self.bottom_field_order_cnt + } else { + self.field = Field::Bottom; + self.pic_order_cnt = self.bottom_field_order_cnt; + + self.top_field_order_cnt + }; + + let second_field = PictureData { + top_field_order_cnt: self.top_field_order_cnt, + bottom_field_order_cnt: self.bottom_field_order_cnt, + frame_num: self.frame_num, + reference: self.reference, + nonexisting: self.nonexisting, + pic_order_cnt: second_pic_order_cnt, + field: self.field.opposite(), + ..Default::default() + }; + + debug!( + "Split into picture (frame_num, POC) ({:?}, {:?}), field: {:?}", + self.frame_num, self.pic_order_cnt, self.field + ); + debug!( + "Split into picture (frame_num, POC) ({:?}, {:?}), field {:?}", + second_field.frame_num, second_field.pic_order_cnt, second_field.field + ); + + let first_field = Rc::new(RefCell::new(self)); + let second_field = Rc::new(RefCell::new(second_field)); + + first_field.borrow_mut().set_second_field_to(&second_field); + second_field.borrow_mut().set_first_field_to(&first_field); + + (RcPictureData { pic: first_field }, RcPictureData { pic: second_field }) + } +} + +impl std::fmt::Debug for PictureData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PictureData") + .field("pic_order_cnt_type", &self.pic_order_cnt_type) + .field("top_field_order_cnt", &self.top_field_order_cnt) + .field("bottom_field_order_cnt", &self.bottom_field_order_cnt) + .field("pic_order_cnt", &self.pic_order_cnt) + .field("pic_order_cnt_msb", &self.pic_order_cnt_msb) + .field("pic_order_cnt_lsb", &self.pic_order_cnt_lsb) + .field("delta_pic_order_cnt_bottom", &self.delta_pic_order_cnt_bottom) + .field("delta_pic_order_cnt0", &self.delta_pic_order_cnt0) + .field("delta_pic_order_cnt1", &self.delta_pic_order_cnt1) + .field("pic_num", &self.pic_num) + .field("long_term_pic_num", &self.long_term_pic_num) + .field("frame_num", &self.frame_num) + .field("frame_num_offset", &self.frame_num_offset) + .field("frame_num_wrap", &self.frame_num_wrap) + .field("long_term_frame_idx", &self.long_term_frame_idx) + .field("coded_resolution", &self.coded_resolution) + .field("display_resolution", &self.display_resolution) + .field("type_", &self.type_) + .field("nal_ref_idc", &self.nal_ref_idc) + .field("is_idr", &self.is_idr) + .field("reference", &self.reference) + .field("ref_pic_list_modification_flag_l0", &self.ref_pic_list_modification_flag_l0) + .field("abs_diff_pic_num_minus1", &self.abs_diff_pic_num_minus1) + .field("has_mmco_5", &self.has_mmco_5) + .field("nonexisting", &self.nonexisting) + .field("field", &self.field) + .field("ref_pic_marking", &self.ref_pic_marking) + .field("field_rank", &self.field_rank) + .finish() + } +} diff --git a/vendor/cros-codecs/src/codec/h264/synthesizer.rs b/vendor/cros-codecs/src/codec/h264/synthesizer.rs new file mode 100644 index 00000000..b28596cb --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/synthesizer.rs @@ -0,0 +1,582 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +use std::fmt; +use std::io::Write; + +use crate::codec::h264::nalu_writer::NaluWriter; +use crate::codec::h264::nalu_writer::NaluWriterError; +use crate::codec::h264::parser::HrdParams; +use crate::codec::h264::parser::NaluType; +use crate::codec::h264::parser::Pps; +use crate::codec::h264::parser::Sps; +use crate::codec::h264::parser::DEFAULT_4X4_INTER; +use crate::codec::h264::parser::DEFAULT_4X4_INTRA; +use crate::codec::h264::parser::DEFAULT_8X8_INTER; +use crate::codec::h264::parser::DEFAULT_8X8_INTRA; + +mod private { + pub trait NaluStruct {} +} + +impl private::NaluStruct for Sps {} + +impl private::NaluStruct for Pps {} + +#[derive(Debug)] +pub enum SynthesizerError { + Unsupported, + NaluWriter(NaluWriterError), +} + +impl fmt::Display for SynthesizerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SynthesizerError::Unsupported => write!(f, "tried to synthesize unsupported settings"), + SynthesizerError::NaluWriter(x) => write!(f, "{}", x.to_string()), + } + } +} + +impl From for SynthesizerError { + fn from(err: NaluWriterError) -> Self { + SynthesizerError::NaluWriter(err) + } +} + +pub type SynthesizerResult = Result; + +/// A helper to output typed NALUs to [`std::io::Write`] using [`NaluWriter`]. +pub struct Synthesizer<'n, N: private::NaluStruct, W: Write> { + writer: NaluWriter, + nalu: &'n N, +} + +/// Extended Sample Aspect Ratio - H.264 Table E-1 +const EXTENDED_SAR: u8 = 255; + +impl Synthesizer<'_, N, W> { + fn u>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> { + self.writer.write_u(bits, value)?; + Ok(()) + } + + fn f>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> { + self.writer.write_f(bits, value)?; + Ok(()) + } + + fn ue>(&mut self, value: T) -> SynthesizerResult<()> { + self.writer.write_ue(value)?; + Ok(()) + } + + fn se>(&mut self, value: T) -> SynthesizerResult<()> { + self.writer.write_se(value)?; + Ok(()) + } + + fn scaling_list(&mut self, list: &[u8], default: &[u8]) -> SynthesizerResult<()> { + // H.264 7.3.2.1.1.1 + if list == default { + self.se(-8)?; + return Ok(()); + } + + // The number of list values we want to encode. + let mut run = list.len(); + + // Check how many values at the end of the matrix are the same, + // so we can save on encoding those. + for j in (1..list.len()).rev() { + if list[j - 1] != list[j] { + break; + } + run -= 1; + } + + // Encode deltas. + let mut last_scale = 8; + for scale in &list[0..run] { + let delta_scale = *scale as i32 - last_scale; + self.se(delta_scale)?; + last_scale = *scale as i32; + } + + // Didn't encode all values, encode -|last_scale| to set decoder's + // |next_scale| (H.264 7.3.2.1.1.1) to zero, i.e. decoder should repeat + // last values in matrix. + if run < list.len() { + self.se(-last_scale)?; + } + + Ok(()) + } + + fn default_scaling_list(i: usize) -> &'static [u8] { + // H.264 Table 7-2 + match i { + 0 => &DEFAULT_4X4_INTRA[..], + 1 => &DEFAULT_4X4_INTRA[..], + 2 => &DEFAULT_4X4_INTRA[..], + 3 => &DEFAULT_4X4_INTER[..], + 4 => &DEFAULT_4X4_INTER[..], + 5 => &DEFAULT_4X4_INTER[..], + 6 => &DEFAULT_8X8_INTRA[..], + 7 => &DEFAULT_8X8_INTER[..], + 8 => &DEFAULT_8X8_INTRA[..], + 9 => &DEFAULT_8X8_INTER[..], + 10 => &DEFAULT_8X8_INTRA[..], + 11 => &DEFAULT_8X8_INTER[..], + _ => unreachable!(), + } + } + + fn rbsp_trailing_bits(&mut self) -> SynthesizerResult<()> { + self.f(1, 1u32)?; + + while !self.writer.aligned() { + self.f(1, 0u32)?; + } + + Ok(()) + } +} + +impl<'n, W: Write> Synthesizer<'n, Sps, W> { + pub fn synthesize( + ref_idc: u8, + sps: &'n Sps, + writer: W, + ep_enabled: bool, + ) -> SynthesizerResult<()> { + let mut s = Self { writer: NaluWriter::::new(writer, ep_enabled), nalu: sps }; + + s.writer.write_header(ref_idc, NaluType::Sps as u8)?; + s.seq_parameter_set_data()?; + s.rbsp_trailing_bits() + } + + fn hrd_parameters(&mut self, hrd_params: &HrdParams) -> SynthesizerResult<()> { + self.ue(hrd_params.cpb_cnt_minus1)?; + self.u(4, hrd_params.bit_rate_scale)?; + self.u(4, hrd_params.cpb_size_scale)?; + + for i in 0..=(hrd_params.cpb_cnt_minus1 as usize) { + self.ue(hrd_params.bit_rate_value_minus1[i])?; + self.ue(hrd_params.cpb_size_value_minus1[i])?; + self.u(1, hrd_params.cbr_flag[i])?; + } + + self.u(5, hrd_params.initial_cpb_removal_delay_length_minus1)?; + self.u(5, hrd_params.cpb_removal_delay_length_minus1)?; + self.u(5, hrd_params.dpb_output_delay_length_minus1)?; + self.u(5, hrd_params.time_offset_length)?; + + Ok(()) + } + + fn vui_parameters(&mut self) -> SynthesizerResult<()> { + // H.264 E.1.1 + let vui_params = &self.nalu.vui_parameters; + + self.u(1, vui_params.aspect_ratio_info_present_flag)?; + if vui_params.aspect_ratio_info_present_flag { + self.u(8, vui_params.aspect_ratio_idc)?; + if vui_params.aspect_ratio_idc == EXTENDED_SAR { + self.u(16, vui_params.sar_width)?; + self.u(16, vui_params.sar_height)?; + } + } + + self.u(1, vui_params.overscan_info_present_flag)?; + if vui_params.overscan_info_present_flag { + self.u(1, vui_params.overscan_appropriate_flag)?; + } + + self.u(1, vui_params.video_signal_type_present_flag)?; + if vui_params.video_signal_type_present_flag { + self.u(3, vui_params.video_format)?; + self.u(1, vui_params.video_full_range_flag)?; + + self.u(1, vui_params.colour_description_present_flag)?; + if vui_params.colour_description_present_flag { + self.u(8, vui_params.colour_primaries)?; + self.u(8, vui_params.transfer_characteristics)?; + self.u(8, vui_params.matrix_coefficients)?; + } + } + + self.u(1, vui_params.chroma_loc_info_present_flag)?; + if vui_params.chroma_loc_info_present_flag { + self.ue(vui_params.chroma_sample_loc_type_top_field)?; + self.ue(self.nalu.vui_parameters.chroma_sample_loc_type_bottom_field)?; + } + + self.u(1, vui_params.timing_info_present_flag)?; + if vui_params.timing_info_present_flag { + self.u(32, vui_params.num_units_in_tick)?; + self.u(32, vui_params.time_scale)?; + self.u(1, vui_params.fixed_frame_rate_flag)?; + } + + self.u(1, vui_params.nal_hrd_parameters_present_flag)?; + if vui_params.nal_hrd_parameters_present_flag { + self.hrd_parameters(&vui_params.nal_hrd_parameters)?; + } + self.u(1, vui_params.vcl_hrd_parameters_present_flag)?; + if vui_params.vcl_hrd_parameters_present_flag { + self.hrd_parameters(&vui_params.vcl_hrd_parameters)?; + } + + if vui_params.nal_hrd_parameters_present_flag || vui_params.vcl_hrd_parameters_present_flag + { + self.u(1, vui_params.low_delay_hrd_flag)?; + } + + self.u(1, vui_params.pic_struct_present_flag)?; + + self.u(1, vui_params.bitstream_restriction_flag)?; + if vui_params.bitstream_restriction_flag { + self.u(1, vui_params.motion_vectors_over_pic_boundaries_flag)?; + self.ue(vui_params.max_bytes_per_pic_denom)?; + self.ue(vui_params.max_bits_per_mb_denom)?; + self.ue(vui_params.log2_max_mv_length_horizontal)?; + self.ue(vui_params.log2_max_mv_length_vertical)?; + self.ue(vui_params.max_num_reorder_frames)?; + self.ue(vui_params.max_dec_frame_buffering)?; + } + + Ok(()) + } + + fn seq_parameter_set_data(&mut self) -> SynthesizerResult<()> { + // H.264 7.3.2.1.1 + self.u(8, self.nalu.profile_idc)?; + self.u(1, self.nalu.constraint_set0_flag)?; + self.u(1, self.nalu.constraint_set1_flag)?; + self.u(1, self.nalu.constraint_set2_flag)?; + self.u(1, self.nalu.constraint_set3_flag)?; + self.u(1, self.nalu.constraint_set4_flag)?; + self.u(1, self.nalu.constraint_set5_flag)?; + self.u(2, /* reserved_zero_2bits */ 0u32)?; + self.u(8, self.nalu.level_idc as u32)?; + self.ue(self.nalu.seq_parameter_set_id)?; + + if self.nalu.profile_idc == 100 + || self.nalu.profile_idc == 110 + || self.nalu.profile_idc == 122 + || self.nalu.profile_idc == 244 + || self.nalu.profile_idc == 44 + || self.nalu.profile_idc == 83 + || self.nalu.profile_idc == 86 + || self.nalu.profile_idc == 118 + || self.nalu.profile_idc == 128 + || self.nalu.profile_idc == 138 + || self.nalu.profile_idc == 139 + || self.nalu.profile_idc == 134 + || self.nalu.profile_idc == 135 + { + self.ue(self.nalu.chroma_format_idc)?; + + if self.nalu.chroma_format_idc == 3 { + self.u(1, self.nalu.separate_colour_plane_flag)?; + } + + self.ue(self.nalu.bit_depth_luma_minus8)?; + self.ue(self.nalu.bit_depth_chroma_minus8)?; + self.u(1, self.nalu.qpprime_y_zero_transform_bypass_flag)?; + self.u(1, self.nalu.seq_scaling_matrix_present_flag)?; + + if self.nalu.seq_scaling_matrix_present_flag { + let scaling_list_count = if self.nalu.chroma_format_idc != 3 { 8 } else { 12 }; + + for i in 0..scaling_list_count { + // Assume if scaling lists are zeroed that they are not present. + if i < 6 { + if self.nalu.scaling_lists_4x4[i] == [0; 16] { + self.u(1, /* seq_scaling_list_present_flag */ false)?; + } else { + self.u(1, /* seq_scaling_list_present_flag */ true)?; + self.scaling_list( + &self.nalu.scaling_lists_4x4[i], + Self::default_scaling_list(i), + )?; + } + } else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] { + self.u(1, /* seq_scaling_list_present_flag */ false)?; + } else { + self.u(1, /* seq_scaling_list_present_flag */ true)?; + self.scaling_list( + &self.nalu.scaling_lists_8x8[i - 6], + Self::default_scaling_list(i), + )?; + } + } + } + } + + self.ue(self.nalu.log2_max_frame_num_minus4)?; + self.ue(self.nalu.pic_order_cnt_type)?; + + if self.nalu.pic_order_cnt_type == 0 { + self.ue(self.nalu.log2_max_pic_order_cnt_lsb_minus4)?; + } else if self.nalu.pic_order_cnt_type == 1 { + self.u(1, self.nalu.delta_pic_order_always_zero_flag)?; + self.se(self.nalu.offset_for_non_ref_pic)?; + self.se(self.nalu.offset_for_top_to_bottom_field)?; + self.ue(self.nalu.num_ref_frames_in_pic_order_cnt_cycle)?; + + for offset_for_ref_frame in &self.nalu.offset_for_ref_frame { + self.se(*offset_for_ref_frame)?; + } + } + + self.ue(self.nalu.max_num_ref_frames)?; + self.u(1, self.nalu.gaps_in_frame_num_value_allowed_flag)?; + self.ue(self.nalu.pic_width_in_mbs_minus1)?; + self.ue(self.nalu.pic_height_in_map_units_minus1)?; + self.u(1, self.nalu.frame_mbs_only_flag)?; + if !self.nalu.frame_mbs_only_flag { + self.u(1, self.nalu.mb_adaptive_frame_field_flag)?; + } + self.u(1, self.nalu.direct_8x8_inference_flag)?; + + self.u(1, self.nalu.frame_cropping_flag)?; + if self.nalu.frame_cropping_flag { + self.ue(self.nalu.frame_crop_left_offset)?; + self.ue(self.nalu.frame_crop_right_offset)?; + self.ue(self.nalu.frame_crop_top_offset)?; + self.ue(self.nalu.frame_crop_bottom_offset)?; + } + + self.u(1, self.nalu.vui_parameters_present_flag)?; + if self.nalu.vui_parameters_present_flag { + self.vui_parameters()?; + } + + Ok(()) + } +} + +impl<'n, W: Write> Synthesizer<'n, Pps, W> { + pub fn synthesize( + ref_idc: u8, + pps: &'n Pps, + writer: W, + ep_enabled: bool, + ) -> SynthesizerResult<()> { + let mut s = Self { writer: NaluWriter::::new(writer, ep_enabled), nalu: pps }; + + s.writer.write_header(ref_idc, NaluType::Pps as u8)?; + s.pic_parameter_set_rbsp()?; + s.rbsp_trailing_bits() + } + + fn pic_parameter_set_rbsp(&mut self) -> SynthesizerResult<()> { + self.ue(self.nalu.pic_parameter_set_id)?; + self.ue(self.nalu.seq_parameter_set_id)?; + self.u(1, self.nalu.entropy_coding_mode_flag)?; + self.u(1, self.nalu.bottom_field_pic_order_in_frame_present_flag)?; + + self.ue(self.nalu.num_slice_groups_minus1)?; + if self.nalu.num_slice_groups_minus1 > 0 { + return Err(SynthesizerError::Unsupported); + } + + self.ue(self.nalu.num_ref_idx_l0_default_active_minus1)?; + self.ue(self.nalu.num_ref_idx_l1_default_active_minus1)?; + self.u(1, self.nalu.weighted_pred_flag)?; + self.u(2, self.nalu.weighted_bipred_idc)?; + self.se(self.nalu.pic_init_qp_minus26)?; + self.se(self.nalu.pic_init_qs_minus26)?; + self.se(self.nalu.chroma_qp_index_offset)?; + self.u(1, self.nalu.deblocking_filter_control_present_flag)?; + self.u(1, self.nalu.constrained_intra_pred_flag)?; + self.u(1, self.nalu.redundant_pic_cnt_present_flag)?; + + if !(self.nalu.transform_8x8_mode_flag + || self.nalu.pic_scaling_matrix_present_flag + || self.nalu.second_chroma_qp_index_offset != 0) + { + return Ok(()); + } + + self.u(1, self.nalu.transform_8x8_mode_flag)?; + self.u(1, self.nalu.pic_scaling_matrix_present_flag)?; + + if self.nalu.pic_scaling_matrix_present_flag { + let mut scaling_list_count = 6; + if self.nalu.transform_8x8_mode_flag { + if self.nalu.sps.chroma_format_idc != 3 { + scaling_list_count += 2; + } else { + scaling_list_count += 6; + } + } + + for i in 0..scaling_list_count { + // Assume if scaling lists are zeroed that they are not present. + if i < 6 { + if self.nalu.scaling_lists_4x4[i] == [0; 16] { + self.u(1, /* seq_scaling_list_present_flag */ false)?; + } else { + self.u(1, /* seq_scaling_list_present_flag */ true)?; + self.scaling_list( + &self.nalu.scaling_lists_4x4[i], + Self::default_scaling_list(i), + )?; + } + } else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] { + self.u(1, /* seq_scaling_list_present_flag */ false)?; + } else { + self.u(1, /* seq_scaling_list_present_flag */ true)?; + self.scaling_list( + &self.nalu.scaling_lists_8x8[i - 6], + Self::default_scaling_list(i), + )?; + } + } + } + + self.se(self.nalu.second_chroma_qp_index_offset)?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + use super::*; + use crate::codec::h264::parser::Nalu; + use crate::codec::h264::parser::NaluType; + use crate::codec::h264::parser::Parser; + use crate::codec::h264::parser::Profile; + + #[test] + fn synthesize_sps() { + let raw_sps_buf = [0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x0a, 0xfb, 0x88]; + let mut raw_sps = Cursor::new(&raw_sps_buf[..]); + + let nalu = Nalu::next(&mut raw_sps).unwrap(); + assert_eq!(nalu.header.type_, NaluType::Sps); + + let mut parser = Parser::default(); + let sps = parser.parse_sps(&nalu).unwrap(); + + let mut buf = Vec::::new(); + Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut buf, false).unwrap(); + + assert_eq!(buf, raw_sps_buf); + + let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); + if write_to_file { + let mut out = std::fs::File::create("sps.h264").unwrap(); + out.write_all(&buf).unwrap(); + out.flush().unwrap(); + } + + let mut cursor = Cursor::new(&buf[..]); + let nalu = Nalu::next(&mut cursor).unwrap(); + + let mut parser = Parser::default(); + + let sps2 = parser.parse_sps(&nalu).unwrap(); + + assert_eq!(sps, sps2); + } + + #[test] + fn synthesize_sps_scaling_lists() { + let sps = Sps { + profile_idc: Profile::High as u8, + seq_scaling_matrix_present_flag: true, + scaling_lists_4x4: [[11, 20, 10, 20, 10, 22, 10, 20, 10, 20, 13, 20, 10, 20, 10, 24]; + 6], + scaling_lists_8x8: [ + [ + 33, 20, 10, 21, 33, 20, 12, 20, 33, 23, 10, 20, 33, 20, 10, 20, 33, 24, 10, 20, + 33, 20, 15, 20, 33, 20, 10, 26, 33, 20, 17, 20, 33, 28, 10, 20, 33, 20, 10, 20, + 33, 29, 10, 20, 33, 20, 11, 20, 33, 20, 10, 20, 33, 20, 10, 20, 33, 20, 10, 20, + 33, 20, 10, 20, + ], + [ + 10, 77, 11, 20, 10, 77, 12, 20, 10, 77, 13, 20, 10, 77, 14, 20, 10, 77, 15, 20, + 10, 77, 16, 20, 10, 77, 17, 20, 10, 77, 18, 20, 10, 77, 19, 20, 10, 77, 10, 20, + 10, 77, 10, 21, 10, 77, 10, 22, 10, 77, 10, 23, 10, 77, 10, 24, 10, 77, 10, 26, + 10, 77, 10, 28, + ], + [0; 64], + [0; 64], + [0; 64], + [0; 64], + ], + frame_mbs_only_flag: true, + ..Default::default() + }; + + let mut buf = Vec::::new(); + Synthesizer::<'_, Sps, _>::synthesize(0, &sps, &mut buf, false).unwrap(); + + let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); + if write_to_file { + let mut out = std::fs::File::create("sps.h264").unwrap(); + out.write_all(&buf).unwrap(); + out.flush().unwrap(); + } + + let mut cursor = Cursor::new(&buf[..]); + let nalu = Nalu::next(&mut cursor).unwrap(); + + let mut parser = Parser::default(); + + let sps2 = parser.parse_sps(&nalu).unwrap(); + + assert_eq!(sps.scaling_lists_4x4, sps2.scaling_lists_4x4); + assert_eq!(sps.scaling_lists_8x8, sps2.scaling_lists_8x8); + } + + #[test] + fn synthesize_pps() { + let raw_sps_pps = [ + 0x00, 0x00, 0x00, 0x01, 0x07, 0x4d, 0x40, 0x0d, 0xa9, 0x18, 0x28, 0x3e, 0x60, 0x0d, + 0x41, 0x80, 0x41, 0xad, 0xb0, 0xad, 0x7b, 0xdf, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, + 0xde, 0x09, 0x88, + ]; + + let mut buf = Vec::::new(); + let mut out = Cursor::new(&mut buf); + + let mut cursor = Cursor::new(&raw_sps_pps[..]); + let mut parser: Parser = Default::default(); + + while let Ok(nalu) = Nalu::next(&mut cursor) { + match nalu.header.type_ { + NaluType::Sps => { + let sps = parser.parse_sps(&nalu).unwrap(); + Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut out, false).unwrap(); + } + NaluType::Pps => { + let pps = parser.parse_pps(&nalu).unwrap(); + Synthesizer::<'_, Pps, _>::synthesize(0, pps, &mut out, false).unwrap(); + } + _ => panic!(), + } + } + + let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); + if write_to_file { + let mut out = std::fs::File::create("sps_pps.h264").unwrap(); + out.write_all(&buf).unwrap(); + out.flush().unwrap(); + + let mut out = std::fs::File::create("sps_pps_ref.h264").unwrap(); + out.write_all(&raw_sps_pps).unwrap(); + out.flush().unwrap(); + } + + assert_eq!(buf, raw_sps_pps); + } +} diff --git a/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264 b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264 new file mode 100644 index 0000000000000000000000000000000000000000..359c373298ed358372108e19069b1ead8dc58a86 GIT binary patch literal 2555 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 BT224} literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.crc b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.crc new file mode 100644 index 00000000..f53b1576 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.crc @@ -0,0 +1,3 @@ +43656b2f +c9dd1361 +62d34555 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.md5 b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.md5 new file mode 100644 index 00000000..70f822bf --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.md5 @@ -0,0 +1,3 @@ +45ba0c1a27d0ff82fd7a969631adffe6 +cee875ded4998c9810a14f9497a11f72 +d1e0b9347134ba7a07cbcf249066f022 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264 b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264 new file mode 100644 index 0000000000000000000000000000000000000000..3132860872504f1bd8f7da40086a56562c55c43d GIT binary patch literal 2930 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 literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.crc b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.crc new file mode 100644 index 00000000..83389050 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.crc @@ -0,0 +1 @@ +7dd66ef1 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.md5 b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.md5 new file mode 100644 index 00000000..180e18a5 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.md5 @@ -0,0 +1 @@ +d2304abbf0349ec63324741bf723960d diff --git a/vendor/cros-codecs/src/codec/h264/test_data/README.md b/vendor/cros-codecs/src/codec/h264/test_data/README.md new file mode 100644 index 00000000..62f33e69 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/README.md @@ -0,0 +1,70 @@ +# H.264 Test Data + +This document lists the test data used by the H.264 decoder. + +Unless otherwise noted, the CRCs were computed using GStreamer's VA-API decoder in +`gst-plugins-bad`. + +## 16x16-I.h264 + +A 16x16 progressive byte-stream encoded I-frame to make it easier to spot errors on the libva trace. +Encoded with the following GStreamer pipeline: + +``` +gst-launch-1.0 videotestsrc num-buffers=1 ! video/x-raw,format=I420,width=16,height=16 ! \ +x264enc ! video/x-h264,profile=constrained-baseline,stream-format=byte-stream ! \ +filesink location="/tmp/16x16-I.h264" +``` + +## 16x16-I-P.h264 + +A 16x16 progressive byte-stream encoded I-frame and P-frame to make it easier to spot errors on the +libva trace. Encoded with the following GStreamer pipeline: + +``` +gst-launch-1.0 videotestsrc num-buffers=2 ! video/x-raw,format=I420,width=16,height=16 ! \ +x264enc b-adapt=false ! video/x-h264,profile=constrained-baseline,stream-format=byte-stream ! \ +filesink location="/tmp/16x16-I-P.h264" +``` + +## 16x16-I-P-B-P.h264 + +A 16x16 progressive byte-stream encoded I-P-B-P sequence to make it easier to it easier to spot +errors on the libva trace. Encoded with the following GStreamer pipeline: + +``` +gst-launch-1.0 videotestsrc num-buffers=3 ! video/x-raw,format=I420,width=16,height=16 ! \ +x264enc b-adapt=false bframes=1 ! video/x-h264,profile=constrained-baseline,stream-format=byte-stream ! \ +filesink location="/tmp/16x16-I-B-P.h264" +``` + +## 16x16-I-P-B-P-high.h264 + +A 16x16 progressive byte-stream encoded I-P-B-P sequence to make it easier to it easier to spot +errors on the libva trace. Also tests whether the decoder supports the high profile. Encoded with +the following GStreamer pipeline: + +``` +gst-launch-1.0 videotestsrc num-buffers=3 ! video/x-raw,format=I420,width=16,height=16 ! \ +x264enc b-adapt=false bframes=1 ! video/x-h264,profile=high,stream-format=byte-stream ! \ +filesink location="/tmp/16x16-I-B-P-high.h264" +``` + +## test-25fps.h264 + +Same as Chromium's `test-25fps.h264`. The slice data in `test-25fps-h264-slice-data-*.bin` was +manually extracted from GStreamer using GDB. + +## test-25fps-interlaced.h264 + +Adapted from Chromium's `test-25fps.h264`. Same file as above, but encoded as interlaced instead +using the following ffmpeg command: + +``` +ffmpeg -i \ +src/third_party/blink/web_tests/media/content/test-25fps.mp4 \ +-flags +ilme+ildct -vbsf h264_mp4toannexb -an test-25fps.h264 +``` + +This test makes sure that the interlaced logic in the decoder actually works, specially that "frame +splitting" works, as the fields here were encoded as frames. diff --git a/vendor/cros-codecs/src/codec/h264/test_data/gen_crcs.sh b/vendor/cros-codecs/src/codec/h264/test_data/gen_crcs.sh new file mode 100755 index 00000000..c0eb7b21 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/gen_crcs.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Generates the CRCs for all .h264 files in the current directory using ffmpeg. + +for f in `ls *.h264`; do + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5 +done diff --git a/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-0.bin b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-0.bin new file mode 100644 index 0000000000000000000000000000000000000000..08b46e225f5ca2f367cc1c6955fc0c5da09a3504 GIT binary patch literal 2235 zcmZXVdpy(oAIJAK*>D&sak9CKLoT`I(kT3XxrU-5^E;K=VMK_sjgmsSo?|&AD#URM zt*bWL4;4{F+HC8%U&c1Hi`n-~kKcd4&*S~i=ks_xU!V8q{dj%eZ{ufrdZ#4>3&$Oh zDA%lFs%utGWhymI(Ky3!jP8Jr`7fI%+iBP!@ih#@(fxC!B+V1nB8)sjo(1(buzuX6`_r__yr&R4gCUd1k zpUC#RC16Xi%Ph8_U_fesP)D}Q(8GHNwGjG7t?+)4NbwP|GTZz$26XJ%MtSpiGc_n# zOk;ts0pR_%%~ zazdA7H{Th)(hCzOzt9prsTN@9?G^JIcz?#S`JB%>KsKeOsr$-Bk8^$>MQ9-m%;JIa zxv5hJau=V~%}OUR?BIq=^(;=^%0x0RZ!C7woDj!L8g?neb;35qJe6p30yFBXRBp)4Vj6MoDW57!;qHdlU<@eAj{dtChf zUuyo^4A{$thvUc!>#a!bvl7)?ZJ-nl zIY`H~IyPm@y|-e^K#(Vt)1>1vJ7ZzqCx}}hSU^=H(_>W@qhy*w83;-Ukn3c6RD3uf z)1=BkaFQ#(mrRFV{vy+qZh@e#J9I)cupzbn-%ZqmOD#4miE7UBh3sRDD%TGLSWYt$ zgwn$1OhhY z*}ZhL*b)IxV%r6@YG^ti*|6j7U;Dy;OVC{7hAOQAz;1869}_D<`i^{r0hbt;_L^*V)RL0L5th zxs;hrfZ3=Y0?Fh1>;INaJ{7xeHrgKn#PmD3zz3e7Y-gs!FhHl6{L$G%JCeD(L^=-v zlr(>mGJlP6e$0``gpMfywc45WYq%kg!qRssFM~K2cbQ;46sAmyTwT^kug5J3XT0qr z{oL$DcRK^FSOU8|_+btVj&VL|3Txl3%ILYTyz`Hdfz7ud@%x1xQ8mX3-}OECx&U|7 zE32z5+NFDgP&y-LVAIfDUOP?k9DZOT6L*aaN?`xSTD({Sh;365N+4}s;kRG6B}p%Q-fcMPDxbE> zx2n(5xgKQLgp94J4eWzf21MyBq}*x7R-_NL1=v!PvWuD9h~M%G&8y3eE}twZY)DOB zdo!Zd9sp((93Uj%{nqly1a}<)HaDegkF_d3Vp!{Cp2|Mj3hOlmySi~ys#8Z-pM%}1dfxeZ|b2SpGtA0b@aMhUA6&oCBvfLBIIa|j-x8ww6`-7!~n zGdDie;S} z5prF!zTI+TYSo!|*E+x;Gy4cZiDp3@GEh3fcANRaQz4>;fs@vkk$ z84VL9KL^Y*o`62-`bu!BRSmE%XI%Pco4YkS?9J=@>HqK7FF#u53|IBmQ?#s!J_kd# zj4cZZx<)T+NKOHhC|P53)HGj#V0SLCsxOhKJ1{IjJ8M$=9{Lt1qfhnT2l_wU1s zmsPR@*9%M{a%SecD~qw5uIeP6d|^tD(~`I8A-@;L+ClIuJOYy2C*Gca`(+|@;eMra ql)PE(pU{C>>RjGMiR#U#PD?+A#cC?dJN%w(d@#WJ#pV~X;Qj-vX@s2s literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-2.bin b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-2.bin new file mode 100644 index 0000000000000000000000000000000000000000..65f72decf24d97d4457c039048fa8479df1f9486 GIT binary patch literal 2429 zcmb`I`BM}27RAG&VUev60a+qcgMtv0wL*Z3vQ!pZ1r&;bY7;=ViYzveghfCe7BniL zNL!izB&k8bCz1lP2_a#T01=WHBzwNk&h$@spPBo^oilUh&N)9^t2s?Lysptl z*B;pnHezao27`A5JCEYKOtkOtrWgX5Y6i>X2qN(?LO~XS0Mo)5a*9&) zTTLNhEt2m7t7BX#g|SBTWb|^N_DX$7ENjg<(`bWkdr2rb4QX6uyIs@6b8^27o3^WU zXHWbM|Kx5I9hJ<(9~ghH8vWf`W9G+xPDQEJn?UkWx^lcQEgrAzG_MHDe@~{5V_bW{ zs>z6VL~BjPQ>sFZ`T2Z5a=tNYm{qN)KPKIHaUrI&Z+no+_6)*QODJ=WweV9A{~P2n zxPq5JPEQocxgVO+3E+)e#4#m-W8B%3wP?}@Se=#Bh}mg?e%T`dm=70()Fwf7rDK)b z8Ndl*tqG_bL`uf_Oak#><3sz?p|9<(u|XN5-DS6gJNRmqfpP~jFSpzGHb}Txycxv_p;4Cs z9ad%G-UU2M=AvV8a~2j!powfGAMA>O_M}>#ax@Lmlga($TNrO`>*@hItIv+6UB5Cp zzlnzgKJ1E8vJ4a04oB_?Bac{u)ID~t(`f+`pw}TK-XB^TsZQPa{#`wQs8w0p(MU9zi4Y9)c@%_}4Ga)|CFX^5 z(C7qI{H?gmsvTW3Q1Bn>5oMpw=jl0+p(5M+!RI~s&4M<23k&(k8o%Txf?}lw@4Xp0 z31M(*uS>SN*HxW$Y)oD7O9q4|kJUsuH|NCJOYLv`d6Lp9j

--o!QH~A{3jAdrc zh>HzWx#nVrl&~^#O9AiiCN0X(BAY0BvR*U#o)K1NLAj=4YT2V1pBR zqnk%Yeo^}=Z2wX)_bpnZS+8l&<370vH_x^5uT1tqV}lYZ_s8yU2fYFu{goPl=-LO0llu|_ znrn4+R$v*I+7AtQZc|U)JArhP39#(NO8vRlEkFc(H;?re9J0PYe_#GfrPEHHDoV!1-Z`H4 z<@atI?zCo0%4tXPNNlSvgO(!4Je5z-M$szD4yhlm9ew&J#x6NPv|LCs+xilFD>F~2 zVo8}o7QWGWa&|fMVE#~yzE^REBn!UsB!pCZB;_dzUO*rwl_m`NWP%1)p6hwlbwH$u2dl2l0*oS& z?~_EOZEfE1Us~SkLy_JoQyo(YJAp2q8nB(R0sNbStn$m`b#aX0UQCiUl4&^Sku`Zr zZVoG%m&1i^^Jjj8_h$PGOwxd3vC7pX7M1Ix<`IT&+Ps~7kt_ug8vxC(&@>6(Y4HV0 zgIF+=7-it3mTshE8l94t*D&NU2Q+6~{o5mLsvz&(Eq^(#!pGkdnN6pX|e`BgHCOthfI1T=letDvMmb z2ORP4c`^dXkmAm&Pg9i;{eAK(l+r^KBX-9BF(qNVi= zfb*~K=T0Ec8G`(&qHVfpVH81>s7e@{Cdd>)Y-NhUfeFG$W;R7Bk(z~|k%w1aynt61 z@pOo^geK*i;s=0#-DLYlz&mZDfO8hd_t)RMdQhzx^H;~c(%1UYXAO@2;P%o}&}Jyu z!cH}}hTyvAd7#+w_DC8N2)T@R9W090*1H-VBSGLJ@vZ~Q5C0ogyd7bq*$X|vXv9CO zL^Xa&y5y)DH@t%5cIWOJ4s1ZpQ6Rb{T{xF@P`Ax!5A#B&kRaU%<)^*M{&u&$2L}MQ zK|<)$ei=R7xe-Z*>@ppDemhIsxnbx%M{X|gHnO&LutoOvM|n)uwBfw;`9%euR^Bk8 z8NNanC(mt^&dc1^H;h|rI!Ovy_Uo<_)_|fNGJD~Qg6lIy?%{Sy zMV!mv-$NLudaAf?ZQ^PtVT`rsq#58re-#2;j-g?(FdK-otKI2=L5m$!fD>2w7As@* j)pt#}>v6jFU$2=1-ooC0|A8S2vx#jOdxSSFF3kEL?3g+` literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-4.bin b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-4.bin new file mode 100644 index 00000000..0327e5c4 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-4.bin @@ -0,0 +1 @@ +`__`}%\jafw[bJA1?' 4_3I?`Wqa 6FzzZ+0I`ݖ2˪}-~ _C 88l50k \ No newline at end of file diff --git a/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264 b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264 new file mode 100644 index 0000000000000000000000000000000000000000..76990939fd82b9784f61ec328ca7a77f6339b17e GIT binary patch literal 124935 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 g5j!Qhb(>mt@j4fQA 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
  • AB06KMf;qLB@1rhpf1#0#qRyTIal8&b8fB?w` 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%0AGi(#vNF+@$~j@-U$vZ8%ajAmb|n*_( zbRoDy*Z46w$<>GeLVH%Pm^j>1jU%x&qP&a)s)2CBO0V};$FFKEyPYUYifrt8q}fmx zJ8bg2Z$A(n&2%F#wKC^Qd5)fQI$^ElqpwtMQO#@(2sJ+`*BP5bzhXlw$_qqhvE+iK zeT-PJKO$>9(QTFHM@jNevSB)3B9$0x!=!N@)TkD$+9jr4WC0La@+4;Yq!~sIH{~tJ z@HMB&RUJ(;bz@}srgowq552vqy6E(36Ve!_o>QU`ydJoR7Q4^9+) z9hlkiu?wDy#C($(s`W>(1)6RTr>y>wqvvr{6D5T4SM*%8F%Ch^grtzdg`ppgEQ#h6 z0a?BDH|bVInwp+>Q98QLl}ncZIIm~fk1IH?$m&cp^mJe$d!hyE$@{-wUannObX@CK zI!IGojSZh!`TqO$UV6;v-yMk@sNuhVmIrSbJg>6qG5~U^6O+8l9q)T0Zq-AX%tg1@ z({h`e__q>y{55+UzP8Yg7)dUdN?O}ohQa*SiGa*v%T%VrSf%bji&YJNOx0@jx zQcxH$15~Um<%vY60Me&by!F%Ju{?<~%O_};c*dg1Sb?Xxtf)WW)-~L`%k5-9-Hl(E za=xxq7g#gG@dCeLFg8z$8E+r>{rg>T;z!Zq1vy$>_FEEw;F`iZo>dwr@mb$~jc0vz zN#0`lgE+&{0tfo{PzuAta!}Lr+mC+GtgF&#SdO!h52NToFXG~)@O~#|L8pOzIAR=} zM2uf^OV|1poE2jkEO*9l&G6#ijdSbOoLk0&C*WLAi@|1qBLmub$rm$3LGC{qy?ysY z-=UjByW$w3cPp2j5e39bbxE_pup0;B^^Izs-ntQFyL!<47mA}!lH^BTNlL{pc$ky> zVV1I|%z=2<<1&7#L*^}DSBI+w9ucV)6prorXAfr|EqbBnQ_aNFff=A9(sypQ-uQ!FO_6Yw3((>AohS8R9*!ChUWlx`^`?q@duI3Ah<&Ob@m+Zk73Qzgs(` zzwY{xRLkhqwFZvgfq-P3I@x~dx^;J0OmdV*$#(oA%?#KrU^DNJV!m;gMfKyi^30vQ zj=ePK-Ki$Oc)a7or0~<4b?vq4`}Aqp zkM?X6ULE8a4=PqW>H9Ogm9{J=dznN|^-Eu9^xyjLyJRovAbbYOl?PcWHV#`XK8Lo6q7h!iHP z$(`J9&g0Oh38m#tw5FXf)j#WXd%j@msF;MWx)6ZIkqP3LXSv_+=({Dn-+Av*WK-Cb7mZCcU?C%H8OpH+HR|h zk>=N#YQBswv3@lh>GW`a>iMBV686DEDh9>ze-fv6r`c8N)#VS|&`F>q#1G+X32Vdn z+0Ib(>1y!{OKDDNhU}DqmJkSCt>`0UUgt}7nOCZO7+0woz8AxUt$X=;+Sp+_PMx7@DygO$eDe_5+#r++pexxEa~vNvX3N4I~kMs;*_-# zvB&>C^Rs6uR<@vM#y&i~J*2F(bq|55u(V!-GUu>@_+*ASSn3h_wss*z|2@Z_k%)JVWRSUPR1$BM3pY5*<5wu51tS zhZd(TBcJE4ob1|&Av+Rsiod7B^df;0>0Ipc@F)SBwS)2gu`pcY@4;zcq8c`$1Og*C z^CLa`9}Qi&^V;AJlDF_!r24%x>o*0d15m^;lYFbR>ou=lIXP3aow=AQuR6?ZSF1^;|fl72v+%A~g|l&-LRn03(* zVx1!px_$k8&;srosWXN~L^$FGB8?|$6Z63y`267B-uq`|zAMTsV(8Y4Px_<%<7D|8 z$;h+z)nxUxr-E1uUqpZ>Ep)~?U{kVNO!V8&H<=k^DASMF@ChoO<_aEueLLEh4D)0kP&KHUPX-7IuZ7G}!`AL`UojykQTBCkCLT%`nLP_6CSx)-s zcmGF0;5;5^&%{)*)IB))uaAGJYX+vxbkDvAD(wC~Wol7v8DT8Y0xZ6)suLVf4+!#F zn>g84VfvBVgDwEm*wE4ehfJ_J!<@(OM7T7Vx3F9r%%ZQ1Fc|V6aOwXjd7L=9S3Bv@ zRq*|gC$v*nDE6c$<*x5>``gn8g-Z}6f({nJQ3{A*kZw0&vDfm zBi{N*O%#Kgp59T&CLm&77=Pqp3azf%gS|lJiT`GA0cV(+cHxD`nw;m02h<>GqGY^L zL}NgkPjcDNOT%LXw#S#YQKpgS3%ru(Tn6JX_6#me*JFm$`F8z1~KIZk|`U# zL+wF1i+g4ZjAGZe{a0L7M56L6_@8piz8~)UO09$FPNrB1CvbQlad(+trloG=} zvNd(m&e@Z?yz;yZB_q!6hP|c!0VjND!1g%3S@!mDV$DUjAXR<_T&e+b7#7PO;bSJh zEzjUb?)&!8fc_*Go)9J_Q#%7885MJ>yV&N_G_!o(2L%8pZ8M#%OMg_XoJ>OPEI7IuCXqk36A_vQQ48sCex}nUKqKWtTu`-(>)< z{(>{@a{u^I99hd)ZGc(+{7}TK5S_>^smUs%x{)t6aU25`EhLTO={r6#L##Z~ zuN6>_^RNGU1qb2KL@z61_(R;#&(}{{?}p6&?2fneN$rnL z&D~~gtOuP>@c7JYAmV>d)Dx1Sxi55mpFwV&?Mg0^bRT4FH8UZbO>4eoyA5be`Rm4f zwl)2TX(93Mo@cE$qgNneuH3}zZ*E;HkF~R@ zPEYyXKUcihJ8O5P#75;)zzLM6LVdFo4CI3eE1iWhVknj$6R zAQI=1S=4sIR46;Sg2q^5zy!#TSYt)=vEewsS~c-TVU9x#rZmJ2sM3bp@u-p=tf2)o zQ)_aq$nC`9maDuFr&;Oq!3v_s0s@qt^`>sDH*_g%>U%_;TsTtY4eX@FmrFJO78boc z{pbfQ`npIoKD3yzo&VXWVD$cV#Pv54sO%&E-3)(W2lQdif>EO9=5%+!8{2@AzhKeNF7KqgiWjw$1D@~? zSTt}`m@@Np;VR2_atVh>-q!W#p85+N3drJlEL)GYncq7VI^_Owx~E^yIUy8X9H)e` z`n-^b3Y?Yf$3a1eOS5cW)jq!VqrrZw)+v}KImiD8r09jXe@BW&&-FjXP`|z#&li{) zST1%O6phI(JLNI~R}bUPJUJEn(;@}?E8|Ach+lqx7o?{={?W;BT$Kqjd}4}hfZn9T zs$&7uYzRoB9ovc?hc5^Z%#J+47v=A%<9S`1W70HGHu_G^GOCsPGr{CnSTr6Ccbd^n zfKc5FW!)DyAL-s+Jc@g^*ShK&WW{drqU>K(fynNm5-^k)6J;guTCR~_ikPHo^=?p# zM~{dXpTRD?9oSD=k8!BN7H+0UJhmXF8uk(wh#<~j_37%Svq*&|{>`wN`S)=06t>9o zF%HvQdYuifvbPiLWyfG=5k51E>-awsGsvsU7MM4M4;ZNEE$2;JFNSy>j&uGS~)#{$;HVHlNW}90M3Iw&NCd*j*9wu19 z7HEBJc=`SfJ<~6MN}!U4(LTmzO+Z$NJF7dtLa>8Wh~$?Huz}Eh-XiXqowi2>j*ggiz4i(^ z-7Mil^FByidce*@fBRtlrX}$Mz>j7q|9;jXm(lOeuFvB}pRxQme)K~j^gz{K=oY@! zCBP78@h&lxQ@voX{wU2q^!(#qHQ#B*;&P z8eg#iFTl{+0koH2Oe|D`#S!Xe|KiiSCPrf)<^DMEISAId-#A_76aZTV}4#j)xp9T zmnu&of(`;k8z9+Iy(3juvv00n^lAP|mCuB(eEYUcW$RR9b%a-~STJRBz5fLpO%`5Q zxVF|2;3NKNDS@9IN0xBE9r)zS|1Q)S6EO~%2I3QlDh#g)EYPZB5~@6#&7#!8d7}$z zII>)!?On~E;19xETOd!)nv{~%vf;ZDHC`x!zZswe^5wT!|ZiJ9>UGT^v&qFxpU3quD_P};N%su!&d6XNI3a~bQ*JQMzDRy;g01IT z-+9>OT~0_8J+ugKdwA0*=ebuNyKp)@IB~0Z}A&E!O$%|A4A>q z-@&8BC;PpDDSS|maHi;{UGAUby?pqHv;t?Hx#LZtZBaIkd&sp|;tG`rH!?c>XRE@4 zXg`|q+Xzgn_zoSmgMdXK{X!}S5LNDyF0 zH#NTsN4+C=Y!vY=R7)@PKTE)(DTYe!+FxM1TG08`*#Ye z|Mb(*&!%s!F(3E4zsCdLm!?+D3P-$VD%b4r58aiFi`9AGJ+Xz55dlAXI{*5NvgKKq zDfBC8BLQYM`;1rKZrnmIo^itHJan!EA(ZNJh%$aJ>>>`k*ZxUfoPd=CrLMX&giXLe z{3FYip(3I9T+oLIVs$BBw#6BI*$<~V?;>DF`^AAU|Gy0%G3=?|Ec%pCCJdu%f3y`w z@MvWqC100)rRx9|+Ms#oONB|r*TEBb#v>F>*zv_kS2T zD;JEw?n07+?tY@XsXLuU)~a#Jbw8NZDuYM%iIpJ#5hKm~&aFFEwA@N5GGRSgI^648 z%ZlI7$pp3tW!qyYnf(H`x~9^9wE62I>jrny_*;8>FY52sIX$I8VP+CKxv$j0R>{{9 zhHLr4^b7iKYh$iEX1Mg5941tHJPLJNak8u=6Od}4@J~snw~INLTdmtr*Ph)RC-ByP zHqwZ-IChXvXzP0Sc^Mtv$<@S)Or}|U*p)4kDcr4+83R-I$Qufen1wL*1`w8#Ah2YpFLtZ(?h0e^gJBtVjoL{m%zBRnDZZh5+6(ChB!TLw~o;vDC&8kpGcmlybw^~fn91;x80I(zIOzN*jJs0qpj1)>o>Sn{K zD@u?|vEM}^2|1=`|1B;(7vv|?TES2Owq6yA-9F+PA=~4Nku0Ym$@=yCPnVByy*MzP zc5pDiwdReiB|u5{?98i@sFN-Vd>r(Sy@JI9>&12~VYzd8ib`D2J;;`hYa60>=rDH& z1^@5ep68Cgx;;xu_s`1CpK4ng#DhZHOI0!u%g&R6I>dR=e&G7wQ__bY$Gvhw^`{F) z5vPQ#uK5Q>4{k3&JaMtU0<)*dun?Gbj#pULZtQC#yT66>W7}>lFJFZhKmcIpR1eL8 zz|jfmK~Ric2is=&YnTi->Lv>=N8?CTcKA|717wY}X*h*Lf4)x zSeWxNDcCXM6vR#h1>>-wHoc{X1g*dB`4}!gPIj1v6i^g$cv+Mpt4VmjS?!+QM8o#L zxxm+IkD+7to82_uOEt}B9)&V`H25A2yd0Wamwkx14T2iZ_crvsLY%(M@`S?PsIc>t z-yL(H;eGR%Mv8Z$ucz^FGQ~c0|AQFpB-gah^BY*%f$#VKK2-Y3^MEU*`)n{cHNLUk zJ9%Mj64dQ`@_W*az9WY6kX^9aMK>4O$JGI;kS?DJRU1*-zN1o;kL5%$D$3N-5zQF zN8a?R-@wy8oZ*-*d*xzrjchyq;i_AQ6ES!yV7Xc*=Irt6yoFnW;Hy*~;XQG()K)W2Y5JY$u(lb$BVxgP}9JV<9}c7*4v7#1LHFq<4Ch znab<%W?&g)UBgtTLnvhoB{9rS*J2|1lv=dUZb|F9n43!M*nvG)&0x<@bC29ig1>OW zIAQ7;m309V;(0AE@_W^2m;oamj_~dwLHCnl2y-hk-hmH%{i4gyIW~)093E%rh`* zm+y2JS)00js63vN``vV>ZHKs=N4Q+lI`iGyR2Ngev`131XT^Ds*_s_7rc0?4d+*wD zY-$yh!c{wQTii_BXGpONaSiiAOU2cq{EA52 zur$wY9Ggie$Lr_Rbvjw^<<2-Ax81j5{3hdRhfpDV;hRK~F1Il+d&-Gw^TNA!q$@86 zYNQXS)`}EUYYkD-<5%nt2eG7n7+tTUs{CA$e($(-$2ixqh|hf%tMpss#;1`=nt?|< zE7#2 zVDRDsvA+dY4-UubL$W?WIe7R#ULmFJp8(BNI|1&bfWUG*LE_eez%D-hzrkI%|9e>V zDk%Ey+XjIE$gF?S4ol}=NZI{}nEhfV`;qzLw6Dg&qA9yCW24BM(TJkCG z@`Wi*?2;j5&l6&j{|qPbhkGS{N|`tszHZp^V=lC}OuhSdU-IS74~g{P3X!?vEA(e} zmvX+(s?Di$9M!Ey+_s1#xG1-Z2a-Pvz_U2$wG=JN@t01QZlnzKxive^!+ouF!AZBH&1izPr6y4fjBDDv@5xx1N&_jhSZf;DaqmTcjU+$uPrMQ4hdaz!W zM{wX8`&vSp<3SY)Ap`Au@3O~p1B23h60<-sj^y7RsU)^9Y=N(n^EGnY^IFfmzKffn zt_29AWQqq}Eb^@a62@Nm7RhGJAVpi?vTOpOuL)-mG z9DWy@Tw%9FH>TJP(C2T6@vz0&Gf{0vE(PLP9Q*X{h4DPIlrj`B55Ea+xIF^)+5?wWY@Bb6e2B0X^7>lwaI}Z3k>GqaT9(27dg3b^k6eQ z{gWBJ?g`r(;65fARF3fMz05L7Ak@hoSo+#h;gHkc7UoW>UmM)wPuamty6ISgzJ2xe3kLNtasFN{_BgmW|(pk z2jH+!rQi~U z7au(Iq&Oq_xvL$Qr?T%|4BT%og4}wvNp~t`@BYf1d2NylU1+kBALZqjs;>V)_u_g_ zgbg<9c?Ilfw(Gn*71>b8ddVR>*mGjn?oLt_i=ucWexc`x@%a3M*P1NH3Q~M~RMQ@Y z+f8~C>S$`8iVe+{n{VqsIXlkGlnI+34W?Jb{V|$B;>r*@q^#Z~C8I#lGmZ4XOvcTXrlUwb{NZyUUvG`E0RxICX?+~#y_AExPL%IilK zAMgvLb(^JVF&IrJ<756}0YJ1 zOyQjzE}Ku7U4!wE$3E8KU5i;v>)Q!KZR}#x51)S2 zH~5jmBlg`2ONs}C53JYpS!tNY&qHnZ(eopq@kGtVQ%R?n{PbTUl%=aUiSymD=BeJRIJE-6m}36ozUGm_}R zmx`}>$DMhyhN_l#bkW6heueWGoa{7jp6-z2x^ZV0BLRGr&7Zw%+Z3Tl_ zjd;^R@dz>N(-d~ktB(J~0jgreMksY<0L}y}k^&iDlwI z<-QoD_(4*SAVRh%8mFrBm+ zJv-rujewY*5Dw5q>pKZG5>znLBYEHWn&AfBcges;{5}yKkC)4{_hUQ^9C|ZyCo5o$ z1{oh`&cdU0v~V_+17vs^_XN|P6HMoUII<8eR3Bc)OmLlu|1EHu8(Zyvb%jN0p+hojJ>@Q^w*7K{_+wX< zvdwh+O}`CIeoy=tU0ahv=$2zvumVO0uYIEWz% zp;c#n&wiWyPoAe^#L>3kDnZn@&NAKiUq`Q9wot?!4O+b4Nxvd-7hCM$!qc&9zZij| zzSZEwh(q79x5(%hwADW_sCNq0Tq}*r^?6}4 zBT2%a&GgWPH!&<0sDoRp-5*PhyFxyfm^hyu`pQ{mqmz9<<1gM&y}SWjFDukY%LtW| zkw>L8M%p+XAxPS4lpANMmD(Xn`})XOHYG?c;8F#IfD+zwUqBkx;UNf#fS!^dDP9JH zS_UXZOrfj&ns7t0U5mqLlD|T2@`YByje5kS^FZM7hV9KBTgG2qDd~Y??6+|81 zt<_TczdivN`b9DHcx746Hcf}j-Ezc;ml2X!SQ)&65MmUJ>6d`|H<@Q{UxYNB-&uc8 zXp^~1xW>EN=^UHf{hgt$#!^kM6wjmw4uzhe+$%``G@hIL77KIvj&p0UY|%zYRCN1i zJuK5xLFi~@E~5NOcVj{pet@WDzVeksi5Ot1PDeKIQYeWeB(r+bQEYBRWbB2p7Z=o7 z3`E(;4`HqiM>zjW5ViGM<(ZAReVqx|>vv9zySt5TJtYqgo+s7jlMC}6iQih14o1x0 zKuq=PEzXN{h%Zk}_xK8(_^513j3H>uUu25jbCihl=?IVr`!*Nf^RosI#ghfr|a_X6MZ9e+dnOl1VWMI05yI1!_8W%>3DlN1Y}14~VMtNx-R~??9tv zT$lbs(P=1@T$%OZL)HdK(I`{1j>_iKg{a11s!7s%W#2rFczD4-r^Gh{uV;u&WT>bI z8z7GN=kIfTbA<0*3k$O}hU7VW-X1(~*;KeFjTdN#RR8eFK<&EyRr!0_vGDu$heZZB>0T`qTV-lbtDCH{k+u`7P#x}Z5~f$8~KW(y#(md+vJ{10EQUwiN(wh zcb-NA;i=>zoef1oKlYa_18VMkUKCHADoR}bjrph)D9gJky=2CgP<$>VB;-n>=7(H^ z%?B@+7h$-<4PKv9;R1tKS{rijna~q*w5d+mFQoBky)XYGr3e)SOhcCfY&G->e`A^! zKw!(bWYHX`u+6LT&EKE+mk9z0b5%P`1}T%gV#P(DPIi`jAx<2e&iO$G)}-)I!xK#I6oZ&ak)>(Ei4Aw(LoO1}p(o1owZkuM zYJwc2d1P6>2^B!@Q2bHE1>+O47&RzACOl7sAE2ric3Y(#fWevdSlLl&zT`dmTX>Ag zlB$)nJAi+8zP54_ZU?8~}9 zcu2bL40^FRgRwRMUAL~U0LQIOTfVvuAiN%g`!Qn~aOywU<9fB$r(S!ibv9TNDZF?r zGFtO~ul&WGK;o^!UIa8 zI;S`YNXa{+3TR;`in7pilIIv(Lt_5DgJ&>UpLc1!xI&}6wX?If2!N*kzdy&$asPFi zF>-{c2lql(PwQG!=}^7}Zk>^sBk2DH@<&A*6v27>Os;sGk*I@Fd%{u{gtf^Mb=o5K zCOQRrWqU_?y7k%NtvP|Gs59Tc=C7)_yPYH&RwXmdPhU8|K~U2R%_)bRTQ!N6fy9sd z9=E`YK;7#SiH6zFqWVO%ta~^N>YoQwg?InABYy5F3I@8D*THB6{KX^; z&g9brzcX4Fkb8U)4AV{y&J1zC2Ze{Qetcw2^Ex(I82W930?~>T4$j8dEokez(Y>7h zNM_#p;Nl1J5||#{qgc8Z;XCJkN@AUV_o?DeXEjRI zrHfoY+i{j2f;!Q%F${yV^}Vz z7M<@Kn;Ok`ius0-+<6IA)+~9fvCG9s6`Guk?!GkPNZtT=O5P}z7gP_# z&`ct9FD_sIEL_LwQ+1mC_GFTqMD!{Btf+`FQ&^_V0EeZ22!*VRXjh~wP%kEVm~Hlw z>=`0e56#q3!&an02QSMY%$8G+$@g1e~ri81{ z!(>95<0gFa=8k>A2$`24^(%pxgUZbA4c`NH%zJp76$8i#Z{VZWa(U=b3sX@4(bC4$02->D=&+PgAiz(fRab9 z=e32~|MDV!G-AHnq<4c|KJxX>OBE!XaspvBE=#fE`QJLCB)U`~0&UaPH5`9CcBUO9CtU_r; zVn%Tfw3kDe&Pyh3nKg|*^92T0bo~HQa0R=_CLYctYjpZ&2Hg^=+A+6lVr~h(5l>I4v{DdMEb~{N0ans-DXze zv_t~O=&FI3rSTnR!SHCHyhHWi2xUinl}gmf7ioIvo?ibQLMdj}9EQL1=@2c&!eHrp z!SnXARjVtX&c8Df-0%V->m9dmI>W@MUhL`yIVY2MqAXIRarGLrMzY9oy@}KD zP1LDeF6JYSSEqN@i$FUXm(CP>=X{@m>4;#(b*7Gyn6Bezb26v&Ktd{WJp6t>ZvQsh zH0>qFpGh!955tPB8#Y2NmXy?>eanNqOofJnCu3D8^8Ht?9yVT>oo4vJ^nGa?XFkBqV|hmbm|MW( z%|vnAS0TAGkMS!W_qqh?^K z>g#32E(ygRc~+*MgulWPwe)Wsu$Ww`J~}wud78vSK&f{*wLW;N#ASYe**TCFEq&36fGe81=!r&5XpcwXOkfCy0 z7Wnel93tyZW^zdlG(jg1Hwcd}gmt(qwUz5&sq9S85y-nxs~Wi_MB`IABlBsC^=sn7 zJAYy#X{*?+`k>{l218ilBEBu&)VAs@6uq_PfVSzv1{~{8xAkm+~vl9DLi$_05XDZb%YmxEpJ zJnPtcU<&|>8TCBIv52M{nK|G`+wIHgQL-kq8O)`OzAzP2fai94J1t||xn6Fdvmq@! z{~QIkFEUl^#pQVp1#sJcN#5`IqJu7RnNk(6J8zT~V*`{=?Z?XQ2gxT^zw&>H-(i)c z2|bJ&g=raZHR_4ddq+y5@zFH7i#tDasU3gyc%0_X@#q$mJi=a(9Yo(^^H4@O;AGER zQIP7FVHb4C>k>e8H|cX(52%wYJ6Or?;oqh@3^ZoDff7e=PtibCchK6=cyUfxZeHD5 ze(jSF*IC$tD3Z9~vm2XCs6-;w1$`Gipj{#T&0-bPU9Ne&Mjp_Li2lvFT-Hr`_XN7a zXU6-m<1hh9FYYXP!V>n0zi!a680N*J-~p=*EZRIhO19TB@Z1qs_Z%Pt4L>KEn$KQ{35(jWwsyVL!@d zlXX$-ZG}38kjE3%2T2{L52)}v-9wUZIawk!0k2!UsVeQG#Kx2LTwD1(DEI-5_lb?r zMhCwSW@@r$1}1tYu1iWUZeCn?9PI137Cwu3dis~}*{vNEp6F*^FX|xxuG>o2RU)^% zq8{p>L3~piXq}|ulAFqdEIYV82%2vsF7U`QFUX54ybjTSbLNVAIg$^ zmuEof{jz?&UPB=ZTgVw>q?L?s9gSJ{Gaasf_&;&FsV%?fbQ9y%6L8q-Cf0qz<4mj* z_+{+hV!BsXXBae<|5*;)i><$^;Ff{Qy}RwRHLXyhK)LtTjTptdkB761CpyfkDR}A`;{ok2xS+3Ud*r*J$ zb61(%^1owpQ};AjY`h3}e0UJvh8Xb&`xyvIg?f6!5eZOMwgfEo++1M0`H9zAXH&3= zLu??~=Tm+UR}ki!R1`8g{&p5VO@q(&N1p%d!mCRThBj*hLvg)R=?b%ch1x}O$i7tec_4B9NK(cby0k(<|?@j&C);aogV>Q1h z9%gqcbuCGGQ%(kwg^s+it*@OCxgIE9;-0lQ#{yi1UoA?K`geuEsg?hj;R9|~E=;Q< zu(~PrEn4LkJ$M~fhS0XA43Npd`vUDdmO<#=4NX+ZRdb$KEZ<=ICgsGq)GkzPkjZ(V zBkp@xZ$Nw+H5>wBEX_2;^Xs4-K!pB%@jc{-gbCb|>h$I*F1w8cw&m0&-)HVR6DKY> z^JzJs+LnFbw?w^1*ZaI)nz}9*3ZggA&l*OHSY2l1$oYs*5}71@gltjx*Gt`~1WR;3 zgd}mEbgEA>!isVJUeCf5f2GoIU;bdrrVrGnr5*lb!EU0xYauKg|w9Y3`} zBcbWODws3%p4*IV6se)9I*oor>~BI*pnqXT-rj%KFC$*~{OKnH?{CG;0P@bC-^t2y z$SbkjA@T%cf>50p*}Fs`!YwIZ1$DD+UMSYtFa4=iX*3esFutVNWcoYj9+_P!N1eW} zZ!|%`pjbGa&nrLczH(PKR4q36rHKEg11JJHH|>^~X%$pZBE|iwwRqFajH39obLlzF z9A|)Fhe25%j*Ao&#|`c}uNsVFs4$4vecRuko*K+~R9f06mF2-5`bpoBGgM)a8|rnb zT9Px+e;;AbvIBi4hQQ8RQc*Z;SRei~!J(x5ca-j-?Xzr5Oio-*onhOMXtx)>WGYfW z#3H?7`lT)c???}sPqKGY$4S#289mtW*XRemQA_w;e@ZVlAi;zFY-_=!Y4w;YdFGuU z4+8K-8qFD_9wo*Rma=!q^-7>TN&f(1UvKD?mKkp#eu#9EN$a}UeQDQ_69^YQTj-0Q z5nR!0m(7S4!8#M}caUCb2%}c>2O-`~!!!yq&Pr7F_;lp%>i#mP0Q?fI<+!;!{M-@o z^)`5X1GxWx)TB1q!nBPDwbaNo&S0L3@MIR3@eZ-N`fJZU;VHL4wFOWV4mWtm*=r`o18u{c)kRoS8)F4v6r*LT|&zdREIr)vZV}ZGUofAZ*K57`*f8CCoz-WyTSv?!s|Cp0^N5 zy5!$O!n^}`HIN$E5()|w1!=${NkEZBAP^x5 zS#EDo+P-Pu>%8fiN$$-3^US%w?fib9uUvuMVnWnCKi>t~U!9Cv%p#V&66-7VKoE?L zF+j;{MvBovPev3?48k*A0CpiTJ?YPdXo zy2mKQ5`r&JJv2z#xFHhO84#q;t|Y$y2L@p2$zKn>l&haXTXi+q%=82_pr5Ao*4*=z z0$p8ob?wu%_Ze=#*SjzH)@_|}_L(EFpn34P7kTD6=J6qYv*Hoxh$O*AM37CT@1MAU z3yRg!Q8<@mvY8+A#;;yEr;JBUwX{D|ZzM0gzas&tVK$83`v89qDfDT{cYQ?yVMO9& zl4mN)LINKEd0KLngcE*(m?%6#Dx+}FKg8vZl{v{DMf)n$<){mKy8%~fp`uG0v57M& zR4n<__ zeIx7l#^5OzSm8)z@FVxzvcRN=DKJ3|&4x}Z` z%|zN=i_6e%h(10ce01X)4PtPu4tqGA9IWX|_Napvq8_hVRJjiavQy}d%2XQi^DSCa z%vqt)SG;3wmZc711xWjBVi=O%Q~txRzH);;A4)>I=<0WC!D>UPGj+I^O{;q~hx`1F zNJowFrkjRRwD>(`qgDPn-;%{6ypmA#!eZ+ntj#>bCfjfOmONTFmk;#3h~5bJ2Pyed zf0!O0-Wf~J?!vSh)BqV0lQ#b4W?c9Z!ZGBEKM%cE3KFLol)0&cbn32w4*0R_-*-7rN~W)NYtN$Ju1a-GeiC^ftM$v5b;bb2?=&z+2 zF7^H(qD8EKT5vX{_MK2s^uP|c&C;a|tV=MwNpK^+2&HtRVgCUp?`iYiAqLlc=15k1 z$u5=sU)bA!8+w;lV#sEys&@HW_S8-ei>$89B8{O*HOUP)JM&-zMo2cmn&`Xwk%-3} z6Mw-q_Xc!{FZIX%PO4`iGps&WY!I+GeQWD8 z#IkachJEYO9`t^|!uV4B>79wtw6>CCQBlXTa*^!NBqSmEDLmR6_VFvFCwTnVWioj@ zC3kqU0fy8|PR(D=P-1N^?5Q*r^jK$5bd`NHv$zVoFnc`VIlN)FIJ-)FxGI7z%&L)w z?s}uLi?DK}$=Fwbbl20T+q?G6oVF$C!w;sf$*UGA9r=UrzRX?F$T zOC00_dM7!kj9adBfap+hzO}#P&}6UQZ7(PHZ~}l+>f9#Ts1@Zl756uV}P2I5vl~@I*SFYvj`xED9^`x*nlm5k> z{SygI!u=a(Hgbf7q@>MbikAwq*VlO7YnYP25v_cQA31LW`~<&xmJEE@6F6?=xoU@d zG0i~BnXhXtVAl+{cNe!niOfdYm!MU2MNux?|598LlJ7Kyt6rFeg93PTrtGrHg{n}6 z)Xda@sQa5&Pt1bo?5DLUt7*zb&oQy0$H0QqTcY2^6Qy%&)h4eWyBWCuySJJ~VD%y= z^M;ua)+lm{<>ROwWJut^IFG|4&@ppU%NQiAJEg3YGv2uF>CyKiw-6*_m%ra*ib=5q zRVX~1v({H^DBuPN!44ysJ1#O;b{=Bvaxf8dh^0*}md=go2@V0Wh@Ar`(} zUk|K1Q`ZQWV+3Vw>a`N+cL^|l;>xx9zd!<3oc{CYA@&MvF-=H{Kb~*>0-fRm7hhyU zyJ>puTaCXIlkye9w67giZ*)=$FBBz^1ESr(F=`ekdnv@V8k`LgZgbstSrc#ZOgnWxy>gdXs$;+78?EcdH^ahcpTJ*0+*J)zeNW)>n!v*KijJHiy5R$ zD2!BKJwqI51lf^-(00*x3oSPhYZ}!zGg>QS*R;h64~*V(PrjfF1XDO(IV;SBhUf3@ z4*$wI_}^?CQ2G)c{L8*oB|^i)Abj|P5< zV}AEgEo=adndod-ZeX}wb(W3O(DB9I-+i}v1mF45$#sSu4C8R_-Oklp&Q`p^?*GZm z(!Pk9R&ZSYok&j_cLRq5%rAdx_<82U*Eh-^5I?&l4dEDjrA-$@xVH0n{CO4LI0S$y$uv*Sf9xmAKw{xX};gU2My`zV@yL z)@D8*W$vLEdU%_^NEX*bj2k6-PZYPr`0X4^GZYFSk o7XrtPjoUcbW5w)u6ao+=2rd~zIWXjsA)aHen4jfM`8(#oZ?Phl6#xJL literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.crc b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.crc new file mode 100644 index 00000000..71110e4f --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.crc @@ -0,0 +1,250 @@ +3eae0fa4 +671432fb +36f2633d +0d4c5ed0 +5401b3b1 +132ea2eb +638474a8 +ddaac73d +55d506dd +a895940a +552aa819 +3df1c8cb +4570f1a7 +e355e911 +29220d98 +e1d6c064 +44b2001e +264b8f82 +d13e3c00 +36b2feb1 +dcee531a +f8a44834 +a913018c +90f486e7 +9a1101d0 +15332185 +842e0474 +bff7bbf9 +dc8cd701 +835bd3cd +2a3ea506 +67e8e53b +ef5b8038 +97c89f86 +ef8a9d7c +792b083e +388e20b0 +c9b83d6c +868e8252 +d77091cc +33a41580 +67912531 +d3a7e0cc +fd5ac6f7 +ed96bee7 +8b4a495c +1219c05f +b8050ff4 +c9c27101 +465b4659 +01fb0b1e +7a01e4d9 +1a6333b7 +bd4e9d8e +fe812caa +4bb09d4d +64b15b3d +cba3b242 +720daf5f +44efaa8c +14ae7ec5 +6c6c8c3d +3bbaa125 +7b4872f6 +a59dd7f2 +83925660 +b0679228 +f52ef9fb +2850560d +01dfd505 +94a48963 +7fcc0cca +47528c61 +032dc2f3 +bb8e0fb1 +63914082 +b9b4c36c +c69f899d +49a9c94e +77af09d5 +07cdcc3d +eabc68d5 +62421f8d +2811bd16 +e3d5a2e5 +17d95adb +55bcd9cb +d785f280 +45e97423 +c7457a9c +3e2b380f +57b67280 +d02339e4 +065e3598 +d5977df8 +8ae372e3 +54e17f4c +ba6e24c1 +a8a11da8 +ed23d1fd +c1f3ed84 +4a46e699 +fea03186 +8bf1c40d +7ee9d985 +fd3fafcc +f2b62184 +aceda460 +97d31d6c +54aa313e +d95b8ea8 +70c3792a +677ee6ad +ca53aba4 +1351a9f5 +99a1bb43 +c01cff4c +b1cb013c +ef543a58 +f5c2ae90 +ca5387ad +7b5ca533 +78d125a3 +5e04f1b8 +8f620bd6 +24ffe5c1 +5fda8ca9 +ae38f186 +2d86f080 +2b94cc5d +697a09f7 +a6187634 +dae9cce8 +74bb20a4 +18a41fd8 +a4d92f33 +e902181c +18b9f932 +e5ee4cda +1ccb55bb +6ae0fe2e +05129a95 +66d634d7 +0dceffae +00814ab6 +6370890a +c6d35660 +8eb21a54 +9ccd4c7e +b3a01706 +79d5382b +d56f2dda +b6bd04c6 +becb8b5d +0860f480 +1febbb26 +c45e567d +9b12f235 +916e5b2b +7a0c2458 +cdfd327b +666d700e +d5743e0e +a8ead497 +2de13dfe +1ef94a75 +8a05fd88 +e8498198 +b3f388bf +3ad2a8ed +9a871a37 +1e994a52 +3c9c0f61 +9e67b705 +b37baee1 +c57c176a +8cd83bd6 +ed5d5bef +d933b481 +75882924 +662a5466 +956e24ed +f55aa48e +16273797 +1dbffc93 +dadd656e +7bec0c75 +aabdd998 +136aa991 +72abfe71 +b2be52f9 +df04bac8 +fc14d7f9 +f34ae6f1 +1ea238aa +d77bd8f7 +821bb618 +4c543487 +81bc3b15 +afd3f64e +843d503a +54bf670f +f447048a +51221d02 +f20efc3b +e492c81f +a2cff836 +41ffd6d6 +8e1877af +a866b3ba +c7751eaf +6d80911d +a8701556 +0185616d +ce91cb8b +a8b341b7 +49c4f47d +d3001a6b +6410d282 +ee71f778 +190d2d8f +05c09706 +e25514d3 +7a8e74df +543ac711 +4477777e +40803303 +3cfae743 +bf691cc7 +c94b4dc2 +7127479d +2aaaa442 +a36b21a2 +f8fcf82a +e024d413 +7e080f9d +937d6382 +3e0a998f +f2af5462 +64df59a0 +9a9d533f +9e2bd916 +58084d1b +d638f9c7 +04d186be +1b3b2b54 +48a8fa7d +d20ab14c +c4b87b7e +0ec6efbf diff --git a/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.json b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.json new file mode 100644 index 00000000..14e8ef24 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.json @@ -0,0 +1,260 @@ +{ + "profile": "H264PROFILE_MAIN", + "width": 320, + "height": 240, + "frame_rate": 25, + "num_frames": 250, + "num_fragments": 258, + "md5_checksums": [ + "a5dad6170eb13fc5cbc6fe3511d44053", + "e056362baaf13dd0f888e67a681ab381", + "ee0c33d2b92e0443ca5770bd0c56911f", + "0c8f0226fd484358b69e9fce6294a888", + "a0809d811b6273bb63ecfdb74097e0df", + "0cada76917c6dbca2093352b3beaa2e9", + "63a5dae178cfa9e10fc6fdae7957f38d", + "7e473898faa27f0372c30ccf7c1702e3", + "6d3e896e1748f259207b2be30d24a0ad", + "7a8a94778f723b6a4da79be367577dd0", + "abbcff2c3d72fe2ccf8205c0e47142cb", + "4790caa46b6c5998d627d53cf8a45ed0", + "6d183cdb8d57e3c5cb5e5340c71214e0", + "2594ab487dcd844860fe120e92e9513b", + "1b365becbe416007e1fb269dfe2bf0d5", + "ae6953f149e85d1170a27e22cf8fea89", + "b6e8a55010fdd0a2a2f9b5aa26f24994", + "0ea165fcde3b1a71bbb2e16c615c9e5a", + "0dd539e2735c216bce229ada7e0e7722", + "9d6b6e80b820a773a16060cd73d0b047", + "f9375adb2bebacaed22305ba32af4583", + "9185b111100dd0431b23ed799ca02b70", + "bdbc5de6197178cc0ef70cdc07b5c34a", + "505517fa76a772b8af27670302b5bb80", + "df7a95b296257c8ec2c9fed399376631", + "8d8d754f34f3118fb0d9ca69da2bf6b0", + "f5d0ef57ef80fb4efffc2d4530f0d60e", + "262eee5c2f052fcff8d330c5aa62353a", + "90ff465117bfc3cc8b6363b204326a0d", + "dc2445266fec9498194b185f4dd41fb2", + "878f7dcb0df9ddeaa8470f73bbf0d1e6", + "848d8910f7c95fbe27bf00d26ce3fc97", + "e01547b08bd83922ccd677733ea21472", + "fdc049f772de5fb88493d8ec58bf808e", + "9f74486171798cbd0d4da6c6bf0ff00f", + "557ff971fa63589cd4ae687abb778e15", + "0fec7b663d136fcdab088178c7c240a2", + "65b1f1ae4a015b9da106ba97baa929f1", + "5e495f76502f2010c30c08d0ea7b6371", + "96067b4e79207c0430f68ab0a4be612b", + "5a5ff39ae498ba16009071e2b19a1047", + "cdf81c7f4c94d199cb252cb0f880ba0f", + "3e482b016234911fda0acb5a2b31b1e8", + "0eba802cbbab73ab3ea6c1713bf38249", + "4e3c939c00b2f439b9bb0e4f748cd693", + "7da28a3926f11c1ad21ada44919326b7", + "bb6ddd70a7c21190a9896deb89f0393f", + "3bbc5a5d4855fdef5cff6a72e080db59", + "8809185828397c4f188225a3a261319e", + "0e6763c0673aad271cd13fc5c6c5b05f", + "7dd4aa0e1a6ac5d308ca8437d3b33420", + "4b38d5173b9e9f03df4c7d02ae380f7c", + "a252d94720017908735d566322be9e25", + "a547bf19701974e914d556b55e5cc876", + "d9f65e78870600cd4b53cb19ecbde3e0", + "22d740be86b5ae270b34a6a05f306169", + "c7d9428660fdffccc6cff0a038a65c8f", + "50dcb76ce04decbc97e17897a03120f5", + "b39d19ae0f6a8dba61eb15f3a27512cf", + "bd1dbca640bef318da29f3d6de5bafc4", + "5eafb79ab0af0235020931cb2b411e97", + "050393b1311b31b5d711cb3a84fa0398", + "f8827e35815f52022a340129bc005860", + "5c6f844ab5b0fab77e98ea3e5a2c6d30", + "f29317ec99a14eeea363fa70d55dfe7a", + "9fe58ebd66d22be9a7a443baa2733f83", + "f7cdb42ccf66afa9cfe598fdd6243869", + "ec68961bf9e81cedb36269a0bf94c851", + "16b64cdafde9e955bf5930c4dbeb2f8e", + "0c412adf668372e1676957e58e876518", + "fb0179693e77dfa0e4873552f81415e0", + "9d31a15e3fe050695d0a7a77387dc7b6", + "737fa9ddc16c371672ab7c43645b69e5", + "3c0723cf264055cdade0834c1fd0c503", + "53b4f854484bab3f7fa8043729e14bf0", + "6fa2c2fd8930dfc46e37ea8a2a153ea5", + "a2d85585fa27d9f000aeed5da5d1722a", + "7da150a3fbfad35c7c7e233147f97927", + "f33db156c0c29a6125904477c7a52e0c", + "8a4b651ac53d128e8bd530f947ecc393", + "0ac0408a68e2fee3f359941c3c7664c4", + "0af391a381b14b44c61e67baf5716869", + "626d9be039035d65f3e6e40f3a1849b7", + "388532467f13b64b84280fc2df75157c", + "79ac74b267d1e31a9760e4797f3bd9af", + "3cb436415cc056b2d2e0b56c5ec99b14", + "66b2a9581b65a9f8381dab9b4e3cb107", + "13f8261ab7bcc21ab46848b861cab446", + "9fd2eb5e88ce9137ba774b8e5743f842", + "531d9b50624f55446efe0ce2eb168cbc", + "be08a1e2b214db9d3b8a7995bdf8c401", + "fbb6f7ff4a4fec07e4609e6292846873", + "70924eb573abb590615100d48f54ef92", + "7dc5a37f2365231da2145dfbcbcf4a1b", + "ca1ef1e6859945533b4afc4bf1a12ab3", + "289ee446a3b431865d539ca2d3a50d59", + "bc1d07902b4572ea615b6653c01f9b52", + "fae9de5e08b65986e9e43df1e5e474e3", + "ea4bb6045444a9c3085dcf546eb71016", + "162392f165140997cf35e436c03366dd", + "1a39e8d92602106ed428487c1e543541", + "383cb9b29e98c471ff4de80a3abfe0cb", + "cc70ac50536aa7b063375423dad34096", + "e2e11c7ade1b414be1e51f626b9939a6", + "2eb1b386c45d627ec119abbc84ea8dd9", + "e788b25c134e1a0f258af9a88b1e1e2f", + "10fb71dee1a7653cb6cc2d19e58b04cb", + "8d20c4bbf93fe920a470f4dfb7f2d130", + "9ff22a54b4589962f1d475a4d308f96f", + "b5f27d12234ab57a97fd8d8595738aac", + "6673fddc74d578fdf5e716218211b7ad", + "e1cc1721e9c048e480214a8cb1369f08", + "cc0d3ce9313fbc02005f4eacf08246f0", + "7e275f2e832e8b4518e2aec5925323e6", + "747af41fea71a7c443b58632ee06b6b7", + "fe8a0cc71b908e241692e36284c9e2a6", + "cdba33dbf114170b84ce01177b1eaa7d", + "0eb286cac90b1e17f4e02c8625df85b9", + "620252c40523dee3f0d3ed75d2d2bfb2", + "ab8165d3282c27f4166c44a3f947154f", + "25cb402b0681d921c819e00e5b8f77ad", + "8ff4db1bb234ca8f2f6f8f4e45dc5088", + "f30db0bbdee5bb63bdf04f5d944352a2", + "985a0d075da06266437d3257e85e6d0f", + "c53c0d7258484d806b908d2c69376688", + "9390be2306908ee719ed6135ae3e0661", + "3f74122552fa39da806b039fdcafe885", + "2c7650ec0a56db5dce916508157d282f", + "44f7fa9c7a71d3968830b2dc97005ed2", + "0e5a240b5b64b1f86db4f603248d2d48", + "dcc1268f74789b61f0120445d86dc3a7", + "1df397a99a63248fe2a283d9398e3bf1", + "72da4e7b2d0b9327bfc198ea86f259ff", + "7152dd7b90eaf1699d5d03a29f41fa96", + "ba8f38171542fd2de0e63fc4dd5ca4d1", + "452e1a61ecef97d95eedbda97f27626d", + "b5163a55f4d3da0cc563f2d8308aa8ea", + "0594c34726be28517ff0180cc826031a", + "0da52fffcf3ab88f9f1e9a0c5b6c277a", + "787b744b305354ac0127af6f7ab6e5b4", + "682d202d5b4b154c1e1d87fea635bfe9", + "b2dbacace18349a8c4bc4af6d0109caf", + "25fbe415ae8031b5f4260222520618f3", + "bab2de7762b7e0db9c8b1583b2ca2fe6", + "1d8b3418ff6170f7492f8b8098dfbe3c", + "89cf144aac4e2932d2b30683ceb8a5df", + "b952fe86a6275745f461e2f0243a8f2f", + "1c5e35bb8d3f117ad22b80c3208ab36b", + "8cba5bec1dbe1e9045cfc458fc3be807", + "e3eb152d091b689b142fb52fde68acfe", + "a90e523e20ef52a9708c27dd32a32e51", + "276c2cb2e4dc2dee97014b913d20ceef", + "bb1c76cafaed84030fbdf88cbd38bd10", + "dd7155ad9cdb7914394f376585716ce4", + "fd088933a0743c3f014c5cde815f8c0d", + "cb41f67f3730d052129aa534e4cae33b", + "349d01c4e208ae68fa5fbfa5c0b259c2", + "44571e3c02368efe154dc942a86db41d", + "9d50ad1788986f0e71aaeed49bfde214", + "9e102fe1eb1cbfd67370cfe2ec676c42", + "16eeab863b12b958d9da04ec16b76049", + "697873e7878031ba7e5fb57fcf8bb3ba", + "7d1c52765bd7dc87b01b96963085eca5", + "dbc235422231280b8b314ac38bf03943", + "bad7ce4deaf3ba172e30ee67770d5495", + "6c7b0cf1bcb5da8ac7805d722d4c8e52", + "92a923806b59470f83a1121c0a2b1282", + "790f8f46429044540879beec078c2b9e", + "fee37fef18b74dfc5fac61dd343297a4", + "adeb2bf5bddd3ee4c3977570f7957eb2", + "42da9b4d6561a9faa4646c1938665d8d", + "96ef3aa375d97217ac52921f11f53393", + "b4b9c2e6dccb82477037b237bc29beb7", + "78f5063f7255796d6b954ffa63f171bc", + "c48f05d7bd5865ba64bb670ff950528c", + "737413a29e706d1ce3cb47476881d72b", + "69da9015e20f16edccf22676850577b8", + "9b76caf25e9566b9a507d2bc9d381d54", + "c7820c8373e48a8219421ce9f01ab923", + "1482abead594e3f5dcc4bc73e5cb3ccd", + "ae8504362f1612eaf12fd4c99fb09849", + "de613970f131a752cceef714c8cee331", + "22a358e5278844f125e72d277397fc91", + "a57f9588335e6afe9b346a674b8c4b02", + "85e88cea5a43e5c7984df07a0b5bed61", + "28ef730a1eade5e972beba4e46273451", + "238dc0244e981538ef2c57c4667164a5", + "7dbead8203f5037fd4ccaf9e662995dc", + "a15b6b70fd457142da0841378b31b9e0", + "e0e51fa5868886349846a60b105721f0", + "afe444099e5c600d3ccaa8711c9099c0", + "67eca422c9f2301e0f9f00674392ff31", + "d21bc2ee616b28d5501334a56570b229", + "93d9d78079d426c690b6c3f707fcc7ab", + "50cc4faf4fa623f78f47865cfd638093", + "89457f854433da6e28fe3e7d921d632a", + "028a95319145051732f732422ad75473", + "ef7124eb0ad577d2fb7d4fbd67146242", + "d5f82be488a5bb797cbfe4449c96b13e", + "06e79dfe8b046868ef7b80eeca6f213c", + "52ecabe5b54e0bcdd9e61855b07403d9", + "ad0f3e305e4c98d31a7aa7b38bd23feb", + "fc8177e47267379c6d86cb66c70a9ecb", + "78f8589a7a57c08808c16bf976e97eb7", + "f32ef46b3a6364450a4c84dc59a3fdb1", + "6dbda2170360e7f601c76ab2d8054ef1", + "d7447d04d50b84be9fb685de827fb799", + "bd678e5ecd23870c85f0d3ac3b304548", + "caa25dceb0de2d4918b62bf290641b36", + "5e231beb0c3c7f29770db53fa311efae", + "dd68639cb5083b06a1ea1c850028cd6f", + "fa1c9525569aba0ef23804b5f586e78b", + "d75607ea7fe696715bf898aa537dc1b2", + "bb0d9294e588699ce1cf9a3e6a477340", + "ef2787371909de8fa59c9f45c41eca96", + "7835e9094edcddfb16b147525cd8792e", + "42dbe4b5aadf06ffca6f24b465b5ba3c", + "36f6aa385cd30d73424113c1a6ff0aad", + "cf2ac7f34a03a9e086858debebafb447", + "9816f6620aafc8cad3a17e089c0bb3e8", + "72db245f772dfcbd40489b96c8f2dd81", + "555b6cb91b77a820359dc17e3a85c7c9", + "88044cb9b0719ef254908df5694daf16", + "82d5c83ae707acc6a24c5e6ea8369a53", + "d6ab871f03fb9c3ebd2c16f7b1cd7778", + "7eb6daaedc6863c8ca86605862cf55fc", + "473e3dc83b70a0639735c211c6fa0fc1", + "3b249d9a5747aaae5d21b54cea85f9f4", + "1e270e073d634b47675716d555a285fc", + "7b2123f7dba790ee1fa3c2f9de87fc19", + "5e331a4678035837e9ed06957d13e05d", + "fc79c2f6c81d68ea24b5f66373ba1b6f", + "a29698a51f90cdeebe78133e9cbb741a", + "e4b88aad03803aa0e4432545b2f4d851", + "c214f459aa112b1d78bc37a599208891", + "b429e6cc68732d804d94929f89c31158", + "bb31ded86e464207d3b0b28b544806ad", + "92bdd6f9949b8ce7e610345551fcfeec", + "a0eccd9dd12fa381f36d0d877c9efa69", + "f205c3e0255e4ff75bd86d3fd4ff0fbb", + "950096d65b623715914809acf4c0a557", + "b87ef8d2938e34894ed1bbf609fb99a2", + "0a303937d3043c39e8904cc0ad181555", + "05458b392f6555d1343949122653d6af", + "d3dd90431ded20aae2f5ff0b65802fa9", + "34df02751bd5cc9efe159cb85d0e22a2", + "b32744532a99fc2657cdfbc85fd87be7", + "160a29e8413328fde6623493a4da522f", + "a2029dbf4304d50965dc2aad44b930c4", + "1c082bc656c7752f21118f10b8677a6c" + ] +} diff --git a/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.md5 b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.md5 new file mode 100644 index 00000000..fc0ca72a --- /dev/null +++ b/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.md5 @@ -0,0 +1,250 @@ +776f98580836e9d1f6de6cd5eaa26541 +b99c6281ace0eeaf0e5718808a568c2b +133d598e8d3bc1ad6dad666799ef3b8c +a0a05e3c48e4351dcf08934e55a53147 +79ecb0b46cf15db887390ee78403c27d +b8ad982874240fee89b026eab547af09 +fb7de18f21faa1480d0f1c50b643b8a5 +a750adaf7e7a955744c377f3ba9bd99e +3b5effc41ded56870353e19aeca14793 +adacf75ba5386d3442d30c8092fb4384 +0fb60fb5707a4b34c4a54f156c532423 +bcafabeb810e85570de61e4bd1b83466 +096823e6e69b2240cfc5f1c08a23d145 +d182e6428b339c9ec59081c4c196c4b5 +0c7edf230a9ca3112cf4895f2af7a835 +f6aca8639f2d838dba473658547a2928 +8eb680b4878e0c9e0bcf089b760b2267 +478b442172760bf7c3c7fe003ad73f21 +10e0f6e66e9739c36caae0cc2eaa97d3 +046a852108fc096d1d553a6583590d4a +45d7284eafcbc5cccc618c12bc6af4c1 +8b5426beaac95a80df290220654c1dbf +226d2eeae31131078f6d257e5ee89285 +2e1774cd1b8776a772b6dcb69773c5eb +f31ec82bece107ec42c68a53b808e949 +8a9ba366d610b45d268848d3176ea179 +17a5556be0efb4a31949857a46cee27e +f2077c849aebec971fa1cbc99d1ee8ee +a067dd84f656e0367c4b3bad1862c533 +8be7cef3ee5b1edca2902e0afb28a116 +5adae2c59438976603e9b4d7da49e210 +44390208c61cda26906a26c85264ed6e +222501ba177acc3aeda64927b0646d4e +0d5fdc758b4632fd7792ce22bcfe717c +144f01c1f347bb9f4dc27df962d37b4f +c88762634ecfa06f1d12a4ce8a025af5 +2f5304392f987f4522e1ed7519475321 +1cbef99d860f038da6b8b2769d6c8e22 +8b5209b0fd0c84b96b9707c77778fe4c +d78469cd4fba6d81b440d1fcb6ed1a71 +11d869672accf917e3f4f706f8f4a9d4 +ef985cbc882bac5bf8639f8d82680e8f +96e113dc4f4d7526b49fb5ea0f8e901d +ce418516be51f76d27509e2fd550f986 +7bd8928c90578e0d2fd8c9c1989946f9 +5a09fc3b51daba42beef52fce8e15881 +ab0430d72c7fd2288830fb99aae787e3 +7a5b68603fd986c7f6027f82fce47cdf +305ec7419408437f8daeff01e3aad6cc +0038fd9154185e9f0a5fded4e8dca4dd +375019f32ed04e8484864f9fc51afd21 +6d8fb78c18042b55884d0a9e8c7ed7ea +f83931a19f8de74dfb93082c228a73bb +04a585694299a82dc862b108f1691dbf +fbc073e705710480225f17d16ccd6527 +1d9e07daeb879c11029397f80513aec9 +3a1ba3f2da5307b17c68803bea449df1 +8d2f9d9a9d1188dbb90b987b321b9cb6 +57c52b47a13cc8aeab3ab005cceb45fd +c52eee2498f6d567bd48e5a7a4046b51 +a401da6fb4349055cf343cd44a974497 +2def25c6377a6c1b069003f838f6ec31 +67331ed57c8a7f189727f84e9a5469ec +8d279d4327fe50f2ff21ee53eb6cf015 +9e5e8223bdc1abbfba3cb65700452b70 +eb211e3d53c1fe49e89aa6ad729fc13c +fc5a3e258868dfb2aedde494f8f475cd +1fe91a15c43a4ad8f7007234de8ac222 +9d43fdbd942765778da18fa223f43cc6 +733b6b775a4a0421fc091216705ef652 +9ab696e0d1dac6105f84ea8ca9465756 +d5686e35b935d8990e5dee6d1bcf4723 +706aba57f814480fa8a17e078b6352f8 +0e5f8befc07ff3317ca174fd587e25bc +f95f242b2e2eafea3f5a20cdf0c34d89 +0f1a8078ce533787c9cb30c9429dd450 +a226c15cc7e0c0b7e1c4de8c620c07b7 +36edee26f6f02c1576019034648cd4c8 +c79dea598375f06c3a24d75f5b77e8cb +eb6fd06a0916ba6d60be7f4ad8561c0b +d022fdb8aa0e7cdc1fd83680b9b8a0fd +b5bb9c5a4cb36aa9c6b4b58076268d3d +38416e5de6f0092bbf61efe8ed7f76cb +a2ce2bf815fffc1fca8d99a4c97afb50 +6beb9dbfdc0edb74498a49c2d81106b3 +7b13ce77fc66031f036a8667076790f2 +569dcc3536bb44110d6b3a54bf0dee88 +6e08f74630a07154df6461ae25a955c9 +af8235bf965e972834a181f9210b9e09 +f9e4b7b0ffbbb24431e2ec6b6b337436 +d87cfba6630357b213316c6608583be5 +ec3382387784e715ea321fc05ce95372 +bc0fef2061b2e64ab5a40e0174a49ea7 +91d25f646606b2f9992688ef4edf4abb +008e89f1070b0d8e2477d7e9fb9a86c9 +69bf6be50b0c1f9305f2522a1b01deef +11db65d54b6c6b6c39062a3beaf82427 +efe6a4e563d48c7c86341a0f30e8fefe +8df7408615732649f592c7830cb0f00e +2fe4ec387ce34336dd8b968dac027b62 +0ae9182a07b24f27298e8eff4a74b541 +c0215167505dc21651dbef4565d0ae9c +4453511aab349d4e9444949ce7b5c138 +29921ddff3b0ba42de2b36249f06a70a +9e8fb2d42c7c90f64c346023186c1d46 +76e252269587a07138b016f57d09d5d4 +a3d3e21942d46be6f98cac225bac9fa1 +47ebd96106eb758c2ab1d7f6325a46ae +fea2c0b6ff517b07aec90c975d3e929f +a58220f49b95e399365f5a9c083923d6 +0ba65b271d8d95d5b1c4ba96a6281e48 +980589673748a3e2cfd37920e330d1e2 +5c800089bdd1626fb2d3b9b51520b2a1 +5aea26dea4436520f9643ed92409daae +48499bbd0c06d1873249ddd2a5dc76a0 +6905e3dc0ee6930b3678d56898bfc5ef +76c17247950a478a67b6adc5e4baa195 +18c123fbb6833207b27e7a400529a607 +9a41b6d8f3c52ebd3a1517fb822d09e1 +10cb9cde61fe48ed38bbe9d50755eaaf +1899fe52ca9524948a641acba633c087 +03b254591b13d993575b960b8a5e5bfe +2cbcffec5b410eb6ecdd05c48e7f0526 +69fd3b0f432bb70404a2a8deddd33856 +f0a8ad93606ae12b229aff1ec0e2c3ee +580ae926ff57ac9ae54f35b000012b97 +87e3e0f6204e2ad502eb5153c65b2632 +d129b9408e42287088d7613d3bfbcd12 +dbc5c280fb9001496ed8b45602b4de1b +f9c1efcff8549db819571d9a6cd55480 +9d0a444c988a2027cbc41c2163be63db +1070020a0a6afb6c597d61b8524381ba +a21ab68bc243e08536feb2624647f7db +2e718c27e0d3266618907e40cc4a28a6 +4669c75778da3184aba250b6310b6d1b +4e841515c3647e65a00b63dee84e67eb +f867fae5ebf1ed714c5a70f3fe10adbc +90cacccf6932ba8660e08b586f7597f7 +cb0774848fc16d1536418ab7e5ada88a +c3e74fbbd564f993828e8cbeb876f31e +90fe0f8363b9a3fb33b9b2bb4db254a4 +d38e11dcb17af7dd7e700355aa92e003 +9bb3f94f84033c4937adf84586e2be95 +4b4aedbc5dfa015b897b765e566feb96 +acd1ceb766b0a4e15f25870f0244f549 +32119d41be592c1f669ffd0b3a7888eb +7e7775b06fa9b72f771ec6b8d481a6d0 +4415957d5a0d1526db1c19bc17b5d395 +334f9ae780437dca2a5fc3377d46f6e5 +35c3b43d748aad62f7410306ea3c3f99 +1ab4c3340132c3f5fa6ef55ba7b08127 +a61818d54edda2029762e87b9b431c34 +1af968ff49189f25e28b9d8afcd46a92 +80a8b78662dcd2aa4b1615b1325757a2 +10ad96ed6e46f5b054603f66ed6bf29b +362457c91a605e04d3334b03b349ec78 +7704dc866db3238023317a0eac5b8249 +4231732950cb318403a45f38a46ad46b +a8482c9229879f410b309fc77983e3d1 +45063084d5e5dfab462adaf5fcb43795 +5028d5d62a02bf9c6843602c9843f54d +bc0d1e086e96d1347cb800a1d1aae611 +343d41fca235543df80e8ecee45f2248 +e68ed7bc5e4f4d5b92202011cdcda940 +d8735dcc4a5b6f28f8724616e256b759 +c261e7a13a9b507af279ed948acbe386 +4d5af215399a10792b02e6a372aadb02 +381abc836088178c39cc7da9b588b5a3 +406e3372afbd23924539b43f6fb9dc63 +032c29160e0529defc053d863b543926 +eb4b9c324770d1f900191833ccc4c603 +7540374c617d048db93719f2f74869e0 +8d851c4ee3ed3b42a8f8abc37dc037d9 +0cc60cb97509cd32f8de9a33248ec2a5 +bcb5a48c5816ecf1c701c1887fd72531 +2d0681c5f0c5daa293ac07c83ef0c852 +936265a67adf4d3bc271d0ef0513c4bf +58e976105d9eb6e0715927143ddfad37 +b6a82350ee6b0f00fb5323b32e8b8dd4 +5cbc1b4da91aed315f7988ee9b4f6ae7 +446945e9f46db22cc480474572957f43 +8cdbbe21312c86754be28c1af186c85f +c696ac596e16de134f9026c2fc89fcb9 +ac368b1a10748d0e18452527fdb67cd9 +f1eb8157957c7a2a84b5310c2825c5fd +7fd5969b27e06110ee5eb52c0b7f895f +93a4c8093abe3a4e37cb317584adec57 +3495c733a5c07428d7014799147f903b +5fb650661b0891f52cc9d908e3425033 +399e846bf54ea08b854adc57b33bb940 +5377988dae4eb18ac1d50e0add7938a1 +f6f14c9520459a1bde9378e1faa59ca0 +93cce870e1a92b6ab841ccf2a27b2770 +7f79cd2127ac7c289414e993005828a6 +ed4d0faa60421d65cbefcc8ea9a83f00 +8c6b723120855441c69ef4ddd7e5194b +9d675f33810dcac032f38e224e4bb65d +a99ad6ea3c77cfb3dd94f8b39a909a34 +9d2f4e964ba17ef2c7d0496231b1c47b +a6f1e6419f610d1f33888bcc7b16c6d1 +f7e92cd6ee5c8aa99f7be89fd4b9f79f +9f95bf8beae3f90eeaa45f8a6003f0aa +361311a47b6cefc08b8e8af37fd68038 +afc33b5877411da4349e013560a15e17 +3811debaef6b08f2e9338e9d04f6f02c +7fd97c64677f845e98a4a0dc97a7711c +7a7392b1f2525842393dba40e92077cb +24ec025a0d2e7c2a57ff336f0a9baba1 +72f4ad6e07a788cd9356f01c92d94c8a +3a801f8e16b8fc40b03be96b9db43e08 +c89e78f74a7dd4c649413129534b9367 +026609682e9ef750ea8e3f196c017c56 +2cc76327407153e015d1ae69f14f643f +9aa38da2abb78d2b237f89ccf3fd052d +e6f44e4485cef73ffa5a649aaeb92293 +ed8a7d1abce5a3f45d771ded4b8d0ddf +b477e09272a812756950266e6d9d21a3 +7f7822c20694075ba7f74a1cc58ed326 +da4bd23e36c0cd2683ad3a08ccb92381 +a9e2a88720bfa413e5e3b2fd4c1306bc +3e91bbfe96a7462186151f6e76e36233 +8fb00375e1d7595fa4544359f349eaed +106d0cc1aad3fa1014b8a34ebe7a4a3c +1692448922564e97d70d04cc12d55e81 +3537b9b91dcae2df5d3a09805a7ffc4d +111ddd889dc595e18f97a93e36f7d3f2 +8e8875c00cfc5394e731e8c2d4846f07 +3f728c9ea9954a093405040047271fb4 +f93ef278c1c3dff430da16ee3596a83f +51c0bd2ac650dd07df5b3630aee3705e +f33a19dd85ef67cdf5a0d92f3f1fbecf +0a6a7457190fa0fa9989dc71fee2152b +f4b157a6ef24d5d2a1027120fc419a91 +dd69ac1443293ea2f753eda6e94101bc +0f431525cb6ac56aa05484cf2a067303 +4aa02c74b3054a4ae3b13a5fbdeccd1f +4f7c553a7830426c493b59685b371223 +738fc54e143b460de42f6279a9ef1672 +a437cbbd9abf89d9e8b1e69b4ef48b86 +afe6cb281c9ce601a1f4ab4d673f734f +4756e9d1ebb100cdf77ff9e6f9d182f6 +a65853ac0ade5103a5840542e6e16c32 +f5abe519e7bf7078d4c581ed477b80bc +d75c1f88e47baeef857fc43e8ad25ba0 +7200f686415f52d0d3ac0684eca69fb2 +31674be1dabca7c3fabef1c7481d6538 +4aa521f1811346604b61a29c658ab020 +fd049c4f80a58b6e2f4aea4d02b941d7 +ef6b331ba064b6637de7e821d5d6d935 +f23fa47c8cc237fa2f878b0bfc508986 diff --git a/vendor/cros-codecs/src/codec/h265.rs b/vendor/cros-codecs/src/codec/h265.rs new file mode 100644 index 00000000..44f3f8a8 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265.rs @@ -0,0 +1,7 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +pub mod dpb; +pub mod parser; +pub mod picture; diff --git a/vendor/cros-codecs/src/codec/h265/dpb.rs b/vendor/cros-codecs/src/codec/h265/dpb.rs new file mode 100644 index 00000000..a9bfb920 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/dpb.rs @@ -0,0 +1,276 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::cell::Ref; +use std::cell::RefCell; +use std::cell::RefMut; +use std::rc::Rc; + +use crate::codec::h265::parser::Sps; +use crate::codec::h265::picture::PictureData; +use crate::codec::h265::picture::Reference; + +// Shortcut to refer to a DPB entry. +// +// The first member of the tuple is the `PictureData` for the frame. +// +// The second member is the backend handle of the frame. +#[derive(Clone, Debug)] +pub struct DpbEntry(pub Rc>, pub T); + +pub struct Dpb { + /// List of `PictureData` and backend handles to decoded pictures. + entries: Vec>, + /// The maximum number of pictures that can be stored. + max_num_pics: usize, +} + +impl Dpb { + /// Returns an iterator over the underlying H265 pictures stored in the + /// DPB. + pub fn pictures(&self) -> impl Iterator> { + self.entries.iter().map(|h| h.0.borrow()) + } + + /// Returns a mutable iterator over the underlying H265 pictures stored in + /// the DPB. + pub fn pictures_mut(&mut self) -> impl Iterator> { + self.entries.iter().map(|h| h.0.borrow_mut()) + } + + /// Returns the length of the DPB. + pub fn len(&self) -> usize { + self.entries.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get a reference to the whole DPB entries. + pub fn entries(&self) -> &Vec> { + &self.entries + } + + /// Set the dpb's max num pics. + pub fn set_max_num_pics(&mut self, max_num_pics: usize) { + self.max_num_pics = max_num_pics; + } + + /// Get a reference to the dpb's max num pics. + pub fn max_num_pics(&self) -> usize { + self.max_num_pics + } + + /// Mark all pictures in the DPB as unused for reference. + pub fn mark_all_as_unused_for_ref(&mut self) { + for mut picture in self.pictures_mut() { + picture.set_reference(Reference::None); + } + } + + /// Gets the position of `needle` in the DPB, if any. + fn get_position(&self, needle: &Rc>) -> Option { + self.entries.iter().position(|handle| Rc::ptr_eq(&handle.0, needle)) + } + + /// Finds a reference picture in the DPB using `poc`. + pub fn find_ref_by_poc(&self, poc: i32) -> Option> { + let position = self.pictures().position(|p| p.is_ref() && p.pic_order_cnt_val == poc); + + log::debug!("find_ref_by_poc: {}, found position {:?}", poc, position); + Some(self.entries[position?].clone()) + } + + /// Finds a reference picture in the DPB using `poc` and `mask`. + pub fn find_ref_by_poc_masked(&self, poc: i32, mask: i32) -> Option> { + let position = + self.pictures().position(|p| p.is_ref() && p.pic_order_cnt_val & mask == poc); + + log::debug!("find_ref_by_poc: {}, found position {:?}", poc, position); + Some(self.entries[position?].clone()) + } + + /// Finds a short term reference picture in the DPB using `poc`. + pub fn find_short_term_ref_by_poc(&self, poc: i32) -> Option> { + let position = self.pictures().position(|p| { + matches!(p.reference(), Reference::ShortTerm) && p.pic_order_cnt_val == poc + }); + + log::debug!("find_short_term_ref_by_poc: {}, found position {:?}", poc, position); + Some(self.entries[position?].clone()) + } + + /// Drains the DPB by continuously invoking the bumping process. + pub fn drain(&mut self) -> Vec> { + log::debug!("Draining the DPB."); + + let mut pics = vec![]; + while let Some(pic) = self.bump(true) { + pics.push(pic); + } + + pics + } + + /// Whether the DPB needs bumping. See C.5.2.2. + pub fn needs_bumping(&mut self, sps: &Sps) -> bool { + let num_needed_for_output = self.pictures().filter(|pic| pic.needed_for_output).count(); + + let highest_tid = sps.max_sub_layers_minus1; + let max_num_reorder_pics = sps.max_num_reorder_pics[usize::from(highest_tid)]; + let max_latency_increase_plus1 = sps.max_latency_increase_plus1[usize::from(highest_tid)]; + let pic_over_max_latency = self.pictures().find(|pic| { + pic.needed_for_output && pic.pic_latency_cnt >= i32::from(max_latency_increase_plus1) + }); + let max_dec_pic_buffering = + usize::from(sps.max_dec_pic_buffering_minus1[usize::from(highest_tid)]) + 1; + + num_needed_for_output > max_num_reorder_pics.into() + || (max_latency_increase_plus1 != 0 && pic_over_max_latency.is_some()) + || self.entries().len() >= max_dec_pic_buffering + } + + /// Find the lowest POC in the DPB that can be bumped. + fn find_lowest_poc_for_bumping(&self) -> Option> { + let lowest = self + .pictures() + .filter(|pic| pic.needed_for_output) + .min_by_key(|pic| pic.pic_order_cnt_val)?; + + let position = self + .entries + .iter() + .position(|handle| handle.0.borrow().pic_order_cnt_val == lowest.pic_order_cnt_val) + .unwrap(); + + Some(self.entries[position].clone()) + } + + /// See C.5.2.4 "Bumping process". + pub fn bump(&mut self, flush: bool) -> Option> { + let handle = self.find_lowest_poc_for_bumping()?; + let mut pic = handle.0.borrow_mut(); + + pic.needed_for_output = false; + log::debug!("Bumping POC {} from the dpb", pic.pic_order_cnt_val); + log::trace!("{:#?}", pic); + + if !pic.is_ref() || flush { + let index = self.get_position(&handle.0).unwrap(); + + log::debug!( + "Removed POC {} from the dpb: reference: {}, flush: {}", + pic.pic_order_cnt_val, + pic.is_ref(), + flush + ); + log::trace!("{:#?}", pic); + + self.entries.remove(index); + } + + Some(handle.clone()) + } + + /// See C.5.2.3. Happens when we are done decoding the picture. + pub fn needs_additional_bumping(&mut self, sps: &Sps) -> bool { + let num_needed_for_output = self.pictures().filter(|pic| pic.needed_for_output).count(); + let highest_tid = sps.max_sub_layers_minus1; + + let max_num_reorder_pics = sps.max_num_reorder_pics[usize::from(highest_tid)]; + let max_latency_increase_plus1 = sps.max_latency_increase_plus1[usize::from(highest_tid)]; + + let pic_over_max_latency = self.pictures().find(|pic| { + pic.needed_for_output && pic.pic_latency_cnt >= i32::from(max_latency_increase_plus1) + }); + + num_needed_for_output > max_num_reorder_pics.into() + || (max_latency_increase_plus1 != 0 && pic_over_max_latency.is_some()) + } + + /// Clears the DPB, dropping all the pictures. + pub fn clear(&mut self) { + log::debug!("Clearing the DPB"); + + let max_num_pics = self.max_num_pics; + + *self = Default::default(); + self.max_num_pics = max_num_pics; + } + + /// Removes all pictures which are marked as "not needed for output" and + /// "unused for reference". See C.5.2.2 + pub fn remove_unused(&mut self) { + log::debug!("Removing unused pictures from DPB."); + self.entries.retain(|e| { + let pic = e.0.borrow(); + let retain = pic.needed_for_output || pic.is_ref(); + log::debug!("Retaining pic POC: {}: {}", pic.pic_order_cnt_val, retain); + retain + }) + } + + /// Store a picture and its backend handle in the DPB. + pub fn store_picture( + &mut self, + picture: Rc>, + handle: T, + ) -> Result<(), String> { + if self.entries.len() >= self.max_num_pics { + return Err("Can't add a picture to the DPB: DPB is full.".into()); + } + + let mut pic = picture.borrow_mut(); + log::debug!( + "Stored picture POC {:?}, the DPB length is {:?}", + pic.pic_order_cnt_val, + self.entries.len() + ); + + if pic.pic_output_flag { + pic.needed_for_output = true; + pic.pic_latency_cnt = 0; + } else { + pic.needed_for_output = false; + } + + // C.3.4. + // After all the slices of the current picture have been decoded, this + // picture is marked as "used for short-term reference". + pic.set_reference(Reference::ShortTerm); + drop(pic); + + for mut pic in self.pictures_mut() { + pic.pic_latency_cnt += 1; + } + + self.entries.push(DpbEntry(picture, handle)); + + Ok(()) + } + + /// Returns all the references in the DPB. + pub fn get_all_references(&self) -> Vec> { + self.entries.iter().filter(|e| e.0.borrow().is_ref()).cloned().collect() + } +} + +impl Default for Dpb { + fn default() -> Self { + // See https://github.com/rust-lang/rust/issues/26925 on why this can't + // be derived. + Self { entries: Default::default(), max_num_pics: Default::default() } + } +} + +impl std::fmt::Debug for Dpb { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let pics = self.entries.iter().map(|h| &h.0).enumerate().collect::>(); + f.debug_struct("Dpb") + .field("pictures", &pics) + .field("max_num_pics", &self.max_num_pics) + .finish() + } +} diff --git a/vendor/cros-codecs/src/codec/h265/parser.rs b/vendor/cros-codecs/src/codec/h265/parser.rs new file mode 100644 index 00000000..c3bb75c0 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/parser.rs @@ -0,0 +1,4784 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! An Annex B h.265 parser. +//! +//! Parses VPSs, SPSs, PPSs and Slices from NALUs. + +use std::collections::BTreeMap; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::rc::Rc; + +use crate::bitstream_utils::BitReader; +use crate::codec::h264::nalu; +use crate::codec::h264::nalu::Header; +use crate::codec::h264::parser::Point; +use crate::codec::h264::parser::Rect; + +// Given the max VPS id. +const MAX_VPS_COUNT: usize = 16; +// Given the max SPS id. +const MAX_SPS_COUNT: usize = 16; +// Given the max PPS id. +const MAX_PPS_COUNT: usize = 64; +// 7.4.7.1 +const MAX_REF_IDX_ACTIVE: u32 = 15; + +// 7.4.3.2.1: +// num_short_term_ref_pic_sets specifies the number of st_ref_pic_set( ) syntax +// structures included in the SPS. The value of num_short_term_ref_pic_sets +// shall be in the range of 0 to 64, inclusive. +// NOTE 5 – A decoder should allocate memory for a total number of +// num_short_term_ref_pic_sets + 1 st_ref_pic_set( ) syntax structures since +// there may be a st_ref_pic_set( ) syntax structure directly signalled in the +// slice headers of a current picture. A st_ref_pic_set( ) syntax structure +// directly signalled in the slice headers of a current picture has an index +// equal to num_short_term_ref_pic_sets. +const MAX_SHORT_TERM_REF_PIC_SETS: usize = 65; + +// 7.4.3.2.1: +const MAX_LONG_TERM_REF_PIC_SETS: usize = 32; + +// From table 7-5. +const DEFAULT_SCALING_LIST_0: [u8; 16] = [16; 16]; + +// From Table 7-6. +const DEFAULT_SCALING_LIST_1: [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, +]; + +// From Table 7-6. +const DEFAULT_SCALING_LIST_2: [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 7-1 – NAL unit type codes and NAL unit type classes +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum NaluType { + #[default] + TrailN = 0, + TrailR = 1, + TsaN = 2, + TsaR = 3, + StsaN = 4, + StsaR = 5, + RadlN = 6, + RadlR = 7, + RaslN = 8, + RaslR = 9, + RsvVclN10 = 10, + RsvVclR11 = 11, + RsvVclN12 = 12, + RsvVclR13 = 13, + RsvVclN14 = 14, + RsvVclR15 = 15, + BlaWLp = 16, + BlaWRadl = 17, + BlaNLp = 18, + IdrWRadl = 19, + IdrNLp = 20, + CraNut = 21, + RsvIrapVcl22 = 22, + RsvIrapVcl23 = 23, + RsvVcl24 = 24, + RsvVcl25 = 25, + RsvVcl26 = 26, + RsvVcl27 = 27, + RsvVcl28 = 28, + RsvVcl29 = 29, + RsvVcl30 = 30, + RsvVcl31 = 31, + VpsNut = 32, + SpsNut = 33, + PpsNut = 34, + AudNut = 35, + EosNut = 36, + EobNut = 37, + FdNut = 38, + PrefixSeiNut = 39, + SuffixSeiNut = 40, + RsvNvcl41 = 41, + RsvNvcl42 = 42, + RsvNvcl43 = 43, + RsvNvcl44 = 44, + RsvNvcl45 = 45, + RsvNvcl46 = 46, + RsvNvcl47 = 47, +} + +impl TryFrom for NaluType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(NaluType::TrailN), + 1 => Ok(NaluType::TrailR), + 2 => Ok(NaluType::TsaN), + 3 => Ok(NaluType::TsaR), + 4 => Ok(NaluType::StsaN), + 5 => Ok(NaluType::StsaR), + 6 => Ok(NaluType::RadlN), + 7 => Ok(NaluType::RadlR), + 8 => Ok(NaluType::RaslN), + 9 => Ok(NaluType::RaslR), + 10 => Ok(NaluType::RsvVclN10), + 11 => Ok(NaluType::RsvVclR11), + 12 => Ok(NaluType::RsvVclN12), + 13 => Ok(NaluType::RsvVclR13), + 14 => Ok(NaluType::RsvVclN14), + 15 => Ok(NaluType::RsvVclR15), + 16 => Ok(NaluType::BlaWLp), + 17 => Ok(NaluType::BlaWRadl), + 18 => Ok(NaluType::BlaNLp), + 19 => Ok(NaluType::IdrWRadl), + 20 => Ok(NaluType::IdrNLp), + 21 => Ok(NaluType::CraNut), + 22 => Ok(NaluType::RsvIrapVcl22), + 23 => Ok(NaluType::RsvIrapVcl23), + 24 => Ok(NaluType::RsvVcl24), + 25 => Ok(NaluType::RsvVcl25), + 26 => Ok(NaluType::RsvVcl26), + 27 => Ok(NaluType::RsvVcl27), + 28 => Ok(NaluType::RsvVcl28), + 29 => Ok(NaluType::RsvVcl29), + 30 => Ok(NaluType::RsvVcl30), + 31 => Ok(NaluType::RsvVcl31), + 32 => Ok(NaluType::VpsNut), + 33 => Ok(NaluType::SpsNut), + 34 => Ok(NaluType::PpsNut), + 35 => Ok(NaluType::AudNut), + 36 => Ok(NaluType::EosNut), + 37 => Ok(NaluType::EobNut), + 38 => Ok(NaluType::FdNut), + 39 => Ok(NaluType::PrefixSeiNut), + 40 => Ok(NaluType::SuffixSeiNut), + 41 => Ok(NaluType::RsvNvcl41), + 42 => Ok(NaluType::RsvNvcl42), + 43 => Ok(NaluType::RsvNvcl43), + 44 => Ok(NaluType::RsvNvcl44), + 45 => Ok(NaluType::RsvNvcl45), + 46 => Ok(NaluType::RsvNvcl46), + 47 => Ok(NaluType::RsvNvcl47), + _ => Err(format!("Invalid NaluType {}", value)), + } + } +} + +impl NaluType { + /// Whether this is an IDR NALU. + pub fn is_idr(&self) -> bool { + matches!(self, Self::IdrWRadl | Self::IdrNLp) + } + + /// Whether this is an IRAP NALU. + pub fn is_irap(&self) -> bool { + let type_ = *self as u32; + type_ >= Self::BlaWLp as u32 && type_ <= Self::RsvIrapVcl23 as u32 + } + + /// Whether this is a BLA NALU. + pub fn is_bla(&self) -> bool { + let type_ = *self as u32; + type_ >= Self::BlaWLp as u32 && type_ <= Self::BlaNLp as u32 + } + + /// Whether this is a CRA NALU. + pub fn is_cra(&self) -> bool { + matches!(self, Self::CraNut) + } + + /// Whether this is a RADL NALU. + pub fn is_radl(&self) -> bool { + matches!(self, Self::RadlN | Self::RadlR) + } + + /// Whether this is a RASL NALU. + pub fn is_rasl(&self) -> bool { + matches!(self, Self::RaslN | Self::RaslR) + } + + //// Whether this is a SLNR NALU. + pub fn is_slnr(&self) -> bool { + // From the specification: + // If a picture has nal_unit_type equal to TRAIL_N, TSA_N, STSA_N, + // RADL_N, RASL_N, RSV_VCL_N10, RSV_VCL_N12 or RSV_VCL_N14, the picture + // is an SLNR picture. Otherwise, the picture is a sub-layer reference + // picture. + matches!( + self, + Self::TrailN + | Self::TsaN + | Self::StsaN + | Self::RadlN + | Self::RaslN + | Self::RsvVclN10 + | Self::RsvVclN12 + | Self::RsvVclN14 + ) + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct NaluHeader { + /// The NALU type. + pub type_: NaluType, + /// Specifies the identifier of the layer to which a VCL NAL unit belongs or + /// the identifier of a layer to which a non-VCL NAL unit applies. + pub nuh_layer_id: u8, + /// Minus 1 specifies a temporal identifier for the NAL unit. The value of + /// nuh_temporal_id_plus1 shall not be equal to 0. + pub nuh_temporal_id_plus1: u8, +} + +impl NaluHeader { + pub fn nuh_temporal_id(&self) -> u8 { + self.nuh_temporal_id_plus1.saturating_sub(1) + } +} + +impl Header for NaluHeader { + fn parse>(cursor: &mut std::io::Cursor) -> Result { + let mut data = [0u8; 2]; + cursor.read_exact(&mut data).map_err(|_| String::from("Broken Data"))?; + let mut r = BitReader::new(&data, false); + let _ = cursor.seek(SeekFrom::Current(-1 * data.len() as i64)); + + // Skip forbidden_zero_bit + r.skip_bits(1)?; + + Ok(Self { + type_: NaluType::try_from(r.read_bits::(6)?)?, + nuh_layer_id: r.read_bits::(6)?, + nuh_temporal_id_plus1: r.read_bits::(3)?, + }) + } + + fn is_end(&self) -> bool { + matches!(self.type_, NaluType::EosNut | NaluType::EobNut) + } + + fn len(&self) -> usize { + // 7.3.1.2 + 2 + } +} + +pub type Nalu<'a> = nalu::Nalu<'a, NaluHeader>; + +/// H265 levels as defined by table A.8. +/// `general_level_idc` and `sub_layer_level_idc[ OpTid ]` shall be set equal to a +/// value of 30 times the level number specified in Table A.8 +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum Level { + #[default] + L1 = 30, + L2 = 60, + L2_1 = 63, + L3 = 90, + L3_1 = 93, + L4 = 120, + L4_1 = 123, + L5 = 150, + L5_1 = 153, + L5_2 = 156, + L6 = 180, + L6_1 = 183, + L6_2 = 186, +} + +impl TryFrom for Level { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 30 => Ok(Level::L1), + 60 => Ok(Level::L2), + 63 => Ok(Level::L2_1), + 90 => Ok(Level::L3), + 93 => Ok(Level::L3_1), + 120 => Ok(Level::L4), + 123 => Ok(Level::L4_1), + 150 => Ok(Level::L5), + 153 => Ok(Level::L5_1), + 156 => Ok(Level::L5_2), + 180 => Ok(Level::L6), + 183 => Ok(Level::L6_1), + 186 => Ok(Level::L6_2), + _ => Err(format!("Invalid Level {}", value)), + } + } +} + +/// H265 profiles. See A.3. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum Profile { + #[default] + Main = 1, + Main10 = 2, + MainStill = 3, + RangeExtensions = 4, + HighThroughput = 5, + MultiviewMain = 6, + ScalableMain = 7, + ThreeDMain = 8, + ScreenContentCoding = 9, + ScalableRangeExtensions = 10, + HighThroughputScreenContentCoding = 11, +} + +impl TryFrom for Profile { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(Profile::Main), + 2 => Ok(Profile::Main10), + 3 => Ok(Profile::MainStill), + 4 => Ok(Profile::RangeExtensions), + 5 => Ok(Profile::HighThroughput), + 6 => Ok(Profile::MultiviewMain), + 7 => Ok(Profile::ScalableMain), + 8 => Ok(Profile::ThreeDMain), + 9 => Ok(Profile::ScreenContentCoding), + 10 => Ok(Profile::ScalableRangeExtensions), + 11 => Ok(Profile::HighThroughputScreenContentCoding), + _ => Err(format!("Invalid Profile {}", value)), + } + } +} + +/// A H.265 Video Parameter Set. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Vps { + /// Identifies the VPS for reference by other syntax elements. + pub video_parameter_set_id: u8, + /// If vps_base_layer_internal_flag is equal to 1 and + /// vps_base_layer_available_flag is equal to 1, the base layer is present + /// in the bitstream. + pub base_layer_internal_flag: bool, + /// See `base_layer_internal_flag`. + pub base_layer_available_flag: bool, + /// Plus 1 specifies the maximum allowed number of layers in each CVS + /// referring to the VPS. + pub max_layers_minus1: u8, + /// Plus 1 specifies the maximum number of temporal sub-layers that may be + /// present in each CVS referring to the VPS. + pub max_sub_layers_minus1: u8, + /// When vps_max_sub_layers_minus1 is greater than 0, specifies whether + /// inter prediction is additionally restricted for CVSs referring to the + /// VPS. + pub temporal_id_nesting_flag: bool, + /// ProfileTierLevel() data. + pub profile_tier_level: ProfileTierLevel, + /// When true, specifies that `vps_max_dec_pic_buffering_minus1[ i ]`, + /// `vps_max_num_reorder_pics[ i ]` and `vps_max_latency_increase_plus1[ i ]` + /// are present for vps_max_sub_layers_ minus1 + 1 sub-layers. + /// vps_sub_layer_ordering_info_present_flag equal to 0 specifies that the + /// values of `vps_max_dec_pic_buffering_minus1[ vps_max_sub_layers_minus1 ]`, + /// vps_max_num_reorder_pics[ vps_max_sub_ layers_minus1 ] and + /// `vps_max_latency_increase_plus1[ vps_max_sub_layers_minus1 ]` apply to all + /// sub-layers + pub sub_layer_ordering_info_present_flag: bool, + /// `max_dec_pic_buffering_minus1[i]` plus 1 specifies the maximum required + /// size of the decoded picture buffer for the CVS in units of picture + /// storage buffers when HighestTid is equal to i. + pub max_dec_pic_buffering_minus1: [u32; 7], + /// Indicates the maximum allowed number of pictures with PicOutputFlag + /// equal to 1 that can precede any picture with PicOutputFlag equal to 1 in + /// the CVS in decoding order and follow that picture with PicOutputFlag + /// equal to 1 in output order when HighestTid is equal to i. + pub max_num_reorder_pics: [u32; 7], + /// When true, `max_latency_increase_plus1[i]` is used to compute the value of + /// `VpsMaxLatencyPictures[ i ]`, which specifies the maximum number of + /// pictures with PicOutputFlag equal to 1 that can precede any picture with + /// PicOutputFlag equal to 1 in the CVS in output order and follow that + /// picture with PicOutputFlag equal to 1 in decoding order when HighestTid + /// is equal to i. + pub max_latency_increase_plus1: [u32; 7], + /// Specifies the maximum allowed value of nuh_layer_id of all NAL units in + /// each CVS referring to the VPS. + pub max_layer_id: u8, + /// num_layer_sets_minus1 plus 1 specifies the number of layer sets that are + /// specified by the VPS. + pub num_layer_sets_minus1: u32, + /// When true, specifies that num_units_in_tick, time_scale, + /// poc_proportional_to_timing_flag and num_hrd_parameters are present in + /// the VPS. + pub timing_info_present_flag: bool, + /// The number of time units of a clock operating at the frequency + /// vps_time_scale Hz that corresponds to one increment (called a clock + /// tick) of a clock tick counter. The value of vps_num_units_in_tick shall + /// be greater than 0. A clock tick, in units of seconds, is equal to the + /// quotient of vps_num_units_in_tick divided by vps_time_scale. For + /// example, when the picture rate of a video signal is 25 Hz, + /// vps_time_scale may be equal to 27 000 000 and vps_num_units_in_tick may + /// be equal to 1 080 000, and consequently a clock tick may be 0.04 + /// seconds. + pub num_units_in_tick: u32, + /// The number of time units that pass in one second. For example, a time + /// coordinate system that measures time using a 27 MHz clock has a + /// vps_time_scale of 27 000 000. + pub time_scale: u32, + /// When true, indicates that the picture order count value for each picture + /// in the CVS that is not the first picture in the CVS, in decoding order, + /// is proportional to the output time of the picture relative to the output + /// time of the first picture in the CVS. When false, indicates that the + /// picture order count value for each picture in the CVS that is not the + /// first picture in the CVS, in decoding order, may or may not be + /// proportional to the output time of the picture relative to the output + /// time of the first picture in the CVS. + pub poc_proportional_to_timing_flag: bool, + /// num_ticks_poc_diff_one_minus1 plus 1 specifies the number of clock ticks + /// corresponding to a difference of picture order count values equal to 1. + pub num_ticks_poc_diff_one_minus1: u32, + /// Specifies the number of hrd_parameters( ) syntax structures present in + /// the VPS RBSP before the vps_extension_flag syntax element. + pub num_hrd_parameters: u32, + /// `hrd_layer_set_idx[ i ]` specifies the index, into the list of layer sets + /// specified by the VPS, of the layer set to which the i-th hrd_parameters( + /// ) syntax structure in the VPS applies. + pub hrd_layer_set_idx: Vec, + /// `cprms_present_flag[ i ]` equal to true specifies that the HRD parameters + /// that are common for all sub-layers are present in the i-th + /// hrd_parameters( ) syntax structure in the VPS. `cprms_present_flag[ i ]` + /// equal to false specifies that the HRD parameters that are common for all + /// sub-layers are not present in the i-th hrd_parameters( ) syntax + /// structure in the VPS and are derived to be the same as the ( i − 1 )-th + /// hrd_parameters( ) syntax structure in the VPS. `cprms_present_flag[ 0 ]` + /// is inferred to be equal to true. + pub cprms_present_flag: Vec, + /// The hrd_parameters() data. + pub hrd_parameters: Vec, + /// When false, specifies that no vps_extension_data_flag syntax elements + /// are present in the VPS RBSP syntax structure. When true, specifies that + /// there are vps_extension_data_flag syntax elements present in the VPS + /// RBSP syntax structure. Decoders conforming to a profile specified in + /// Annex A but not supporting the INBLD capability specified in Annex F + /// shall ignore all data that follow the value 1 for vps_extension_flag in + /// a VPS NAL unit. + pub extension_flag: bool, +} + +impl Default for Vps { + fn default() -> Self { + Self { + video_parameter_set_id: Default::default(), + base_layer_internal_flag: Default::default(), + base_layer_available_flag: Default::default(), + max_layers_minus1: Default::default(), + max_sub_layers_minus1: Default::default(), + temporal_id_nesting_flag: Default::default(), + profile_tier_level: Default::default(), + sub_layer_ordering_info_present_flag: Default::default(), + max_dec_pic_buffering_minus1: Default::default(), + max_num_reorder_pics: Default::default(), + max_latency_increase_plus1: Default::default(), + max_layer_id: Default::default(), + num_layer_sets_minus1: Default::default(), + timing_info_present_flag: Default::default(), + num_units_in_tick: Default::default(), + time_scale: Default::default(), + poc_proportional_to_timing_flag: Default::default(), + num_ticks_poc_diff_one_minus1: Default::default(), + num_hrd_parameters: Default::default(), + hrd_layer_set_idx: Default::default(), + cprms_present_flag: vec![true], + hrd_parameters: Default::default(), + extension_flag: Default::default(), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ProfileTierLevel { + /// Specifies the context for the interpretation of general_profile_idc and + /// `general_profile_compatibility_flag[ j ]` for all values of j in the range + /// of 0 to 31, inclusive. + pub general_profile_space: u8, + /// Specifies the tier context for the interpretation of general_level_idc + /// as specified in Annex A. + pub general_tier_flag: bool, + /// When general_profile_space is equal to 0, indicates a profile to which + /// the CVS conforms as specified in Annex A. Bitstreams shall not contain + /// values of general_profile_idc other than those specified in Annex A. + /// Other values of general_profile_idc are reserved for future use by ITU-T + /// | ISO/IEC. + pub general_profile_idc: u8, + /// `general_profile_compatibility_flag[ j ]` equal to true, when + /// general_profile_space is false, indicates that the CVS conforms to the + /// profile indicated by general_profile_idc equal to j as specified in + /// Annex A. + pub general_profile_compatibility_flag: [bool; 32], + /// general_progressive_source_flag and general_interlaced_source_flag are + /// interpreted as follows: + /// + /// –If general_progressive_source_flag is true and + /// general_interlaced_source_flag is false, the source scan type of the + /// pictures in the CVS should be interpreted as progressive only. + /// + /// –Otherwise, if general_progressive_source_flag is false and + /// general_interlaced_source_flag is true, the source scan type of the + /// pictures in the CVS should be interpreted as interlaced only. + /// + /// –Otherwise, if general_progressive_source_flag is false and + /// general_interlaced_source_flag is false, the source scan type of the + /// pictures in the CVS should be interpreted as unknown or unspecified. + /// + /// –Otherwise (general_progressive_source_flag is true and + /// general_interlaced_source_flag is true), the source scan type of each + /// picture in the CVS is indicated at the picture level using the syntax + /// element source_scan_type in a picture timing SEI message. + pub general_progressive_source_flag: bool, + /// See `general_progressive_source_flag`. + pub general_interlaced_source_flag: bool, + /// If true, specifies that there are no frame packing arrangement SEI + /// messages, segmented rectangular frame packing arrangement SEI messages, + /// equirectangular projection SEI messages, or cubemap projection SEI + /// messages present in the CVS. If false, indicates that there may or may + /// not be one or more frame packing arrangement SEI messages, segmented + /// rectangular frame packing arrangement SEI messages, equirectangular + /// projection SEI messages, or cubemap projection SEI messages present in + /// the CVS. + pub general_non_packed_constraint_flag: bool, + /// When true, specifies that field_seq_flag is false. When false, indicates + /// that field_seq_flag may or may not be false. + pub general_frame_only_constraint_flag: bool, + /// See Annex A. + pub general_max_12bit_constraint_flag: bool, + /// See Annex A. + pub general_max_10bit_constraint_flag: bool, + /// See Annex A. + pub general_max_8bit_constraint_flag: bool, + /// See Annex A. + pub general_max_422chroma_constraint_flag: bool, + /// See Annex A. + pub general_max_420chroma_constraint_flag: bool, + /// See Annex A. + pub general_max_monochrome_constraint_flag: bool, + /// See Annex A. + pub general_intra_constraint_flag: bool, + /// See Annex A. + pub general_lower_bit_rate_constraint_flag: bool, + /// See Annex A. + pub general_max_14bit_constraint_flag: bool, + /// See Annex A. + pub general_one_picture_only_constraint_flag: bool, + /// When true, specifies that the INBLD capability as specified in Annex F + /// is required for decoding of the layer to which the profile_tier_level( ) + /// syntax structure applies. When false, specifies that the INBLD + /// capability as specified in Annex F is not required for decoding of the + /// layer to which the profile_tier_level( ) syntax structure applies. + pub general_inbld_flag: bool, + /// Indicates a level to which the CVS conforms as specified in Annex A. + pub general_level_idc: Level, + /// Sub-layer syntax element. + pub sub_layer_profile_present_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_level_present_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_profile_space: [u8; 6], + /// Sub-layer syntax element. + pub sub_layer_tier_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_profile_idc: [u8; 6], + /// Sub-layer syntax element. + pub sub_layer_profile_compatibility_flag: [[bool; 32]; 6], + /// Sub-layer syntax element. + pub sub_layer_progressive_source_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_interlaced_source_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_non_packed_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_frame_only_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_max_12bit_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_max_10bit_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_max_8bit_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_max_422chroma_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_max_420chroma_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_max_monochrome_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_intra_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_one_picture_only_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_lower_bit_rate_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_max_14bit_constraint_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_inbld_flag: [bool; 6], + /// Sub-layer syntax element. + pub sub_layer_level_idc: [Level; 6], +} + +impl ProfileTierLevel { + pub fn max_luma_ps(&self) -> u32 { + // See Table A.8. + match self.general_level_idc { + Level::L1 => 36864, + Level::L2 => 122880, + Level::L2_1 => 245760, + Level::L3 => 552960, + Level::L3_1 => 983040, + Level::L4 | Level::L4_1 => 2228224, + Level::L5 | Level::L5_1 | Level::L5_2 => 8912896, + _ => 35651584, + } + } + + pub fn max_dpb_pic_buf(&self) -> u32 { + if self.general_profile_idc >= 1 && self.general_profile_idc <= 5 { + 6 + } else { + 7 + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SpsRangeExtension { + pub transform_skip_rotation_enabled_flag: bool, + pub transform_skip_context_enabled_flag: bool, + pub implicit_rdpcm_enabled_flag: bool, + pub explicit_rdpcm_enabled_flag: bool, + pub extended_precision_processing_flag: bool, + pub intra_smoothing_disabled_flag: bool, + pub high_precision_offsets_enabled_flag: bool, + pub persistent_rice_adaptation_enabled_flag: bool, + pub cabac_bypass_alignment_enabled_flag: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SpsSccExtension { + /// When set, specifies that a picture in the CVS may be included in a + /// reference picture list of a slice of the picture itself. When not set, + /// specifies that a picture in the CVS is never included in a reference + /// picture list of a slice of the picture itself. + pub curr_pic_ref_enabled_flag: bool, + /// When set, specifies that the decoding process for palette mode may be + /// used for intra blocks. When not set, specifies that the decoding process + /// for palette mode is not applied. + pub palette_mode_enabled_flag: bool, + /// Specifies the maximum allowed palette size. + pub palette_max_size: u8, + /// Specifies the difference between the maximum allowed palette predictor + /// size and the maximum allowed palette size. + pub delta_palette_max_predictor_size: u8, + /// When set, specifies that the sequence palette predictors are initialized + /// using the sps_palette_predictor_initializers. When not set, specifies + /// that the entries in the sequence palette predictor are initialized to 0. + pub palette_predictor_initializers_present_flag: bool, + /// num_palette_predictor_initializers_minus1 plus 1 specifies the number of + /// entries in the sequence palette predictor initializer. + pub num_palette_predictor_initializer_minus1: u8, + /// `palette_predictor_initializer[ comp ][ i ]` specifies the value of the + /// comp-th component of the i-th palette entry in the SPS that is used to + /// initialize the array PredictorPaletteEntries. + pub palette_predictor_initializer: [[u32; 128]; 3], + /// Controls the presence and inference of the use_integer_mv_flag that + /// specifies the resolution of motion vectors for inter prediction. + pub motion_vector_resolution_control_idc: u8, + /// When set, specifies that the intra boundary filtering process is + /// unconditionally disabled for intra prediction. If not set, specifies + /// that the intra boundary filtering process may be used. + pub intra_boundary_filtering_disabled_flag: bool, +} + +impl Default for SpsSccExtension { + fn default() -> Self { + Self { + curr_pic_ref_enabled_flag: Default::default(), + palette_mode_enabled_flag: Default::default(), + palette_max_size: Default::default(), + delta_palette_max_predictor_size: Default::default(), + palette_predictor_initializers_present_flag: Default::default(), + num_palette_predictor_initializer_minus1: Default::default(), + palette_predictor_initializer: [[0; 128]; 3], + motion_vector_resolution_control_idc: Default::default(), + intra_boundary_filtering_disabled_flag: Default::default(), + } + } +} + +/// A H.265 Sequence Parameter Set. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Sps { + /// Specifies the value of the vps_video_parameter_set_id of the active VPS. + pub video_parameter_set_id: u8, + /// `max_sub_layers_minus1` plus 1 specifies the maximum number of temporal + /// sub-layers that may be present in each CVS referring to the SPS. + pub max_sub_layers_minus1: u8, + /// When sps_max_sub_layers_minus1 is greater than 0, specifies whether + /// inter prediction is additionally restricted for CVSs referring to the + /// SPS. + pub temporal_id_nesting_flag: bool, + /// profile_tier_level() data. + pub profile_tier_level: ProfileTierLevel, + /// Provides an identifier for the SPS for reference by other syntax + /// elements. + pub seq_parameter_set_id: u8, + /// Specifies the chroma sampling relative to the luma sampling as specified + /// in clause 6.2. + pub chroma_format_idc: u8, + /// When true, specifies that the three colour components of the 4:4:4 + /// chroma format are coded separately. When false, specifies that the + /// colour components are not coded separately. + pub separate_colour_plane_flag: bool, + /// Specifies the width of each decoded picture in units of luma samples. + pub pic_width_in_luma_samples: u16, + /// Specifies the height of each decoded picture in units of luma samples. + pub pic_height_in_luma_samples: u16, + /// When true, indicates that the conformance cropping window offset + /// parameters follow next in the SPS. When false, indicates that the + /// conformance cropping window offset parameters are not present. + pub conformance_window_flag: bool, + /* if conformance_window_flag */ + /// Specify the samples of the pictures in the CVS that are output from the + /// decoding process, in terms of a rectangular region specified in picture + /// coordinates for output. + pub conf_win_left_offset: u32, + pub conf_win_right_offset: u32, + pub conf_win_top_offset: u32, + pub conf_win_bottom_offset: u32, + + /// Specifies the bit depth of the samples of the luma array BitDepthY and + /// the value of the luma quantization parameter range offset QpBdOffsetY. + pub bit_depth_luma_minus8: u8, + /// Specifies the bit depth of the samples of the chroma arrays BitDepthC + /// and the value of the chroma quantization parameter range offset + /// QpBdOffsetC. + pub bit_depth_chroma_minus8: u8, + /// Specifies the value of the variable MaxPicOrderCntLsb that is used in + /// the decoding process for picture order count. + pub log2_max_pic_order_cnt_lsb_minus4: u8, + /// When true, specifies that `max_dec_pic_buffering_minus1[ i ]`, + /// `max_num_reorder_pics[ i ]` and `max_latency_increase_plus1[ i ]` are + /// present for max_sub_layers_minus1 + 1 sub- layers. When false, specifies + /// that the values of `max_dec_pic_ buffering_minus1[ max_sub_layers_minus1 + /// ]`, `max_num_reorder_pics[ max_sub_layers_minus1 ]` and max_ + /// `latency_increase_plus1[ max_sub_layers_minus1 ]` apply to all sub-layers. + pub sub_layer_ordering_info_present_flag: bool, + /// `max_dec_pic_buffering_minus1[ i ]` plus 1 specifies the maximum required + /// size of the decoded picture buffer for the CVS in units of picture + /// storage buffers when HighestTid is equal to i. + pub max_dec_pic_buffering_minus1: [u8; 7], + /// `max_num_reorder_pics[ i ]` indicates the maximum allowed number of + /// pictures with PicOutputFlag equal to 1 that can precede any picture with + /// PicOutputFlag equal to 1 in the CVS in decoding order and follow that + /// picture with PicOutputFlag equal to 1 in output order when HighestTid is + /// equal to i. + pub max_num_reorder_pics: [u8; 7], + /// `max_latency_increase_plus1[ i ]` not equal to 0 is used to compute the + /// value of `SpsMaxLatencyPictures[ i ]`, which specifies the maximum number + /// of pictures with PicOutputFlag equal to 1 that can precede any picture + /// with PicOutputFlag equal to 1 in the CVS in output order and follow that + /// picture with PicOutputFlag equal to 1 in decoding order when HighestTid + /// is equal to i. + pub max_latency_increase_plus1: [u8; 7], + /// min_luma_coding_block_size_minus3 plus 3 specifies the minimum luma + /// coding block size. + pub log2_min_luma_coding_block_size_minus3: u8, + /// Specifies the difference between the maximum and minimum luma coding + /// block size. + pub log2_diff_max_min_luma_coding_block_size: u8, + /// min_luma_transform_block_size_minus2 plus 2 specifies the minimum luma + /// transform block size. + pub log2_min_luma_transform_block_size_minus2: u8, + /// Specifies the difference between the maximum and minimum luma transform + /// block size. + pub log2_diff_max_min_luma_transform_block_size: u8, + /// Specifies the maximum hierarchy depth for transform units of coding + /// units coded in inter prediction mode. + pub max_transform_hierarchy_depth_inter: u8, + /// Specifies the maximum hierarchy depth for transform units of coding + /// units coded in intra prediction mode. + pub max_transform_hierarchy_depth_intra: u8, + /// When true, specifies that a scaling list is used for the scaling process + /// for transform coefficients. When false, specifies that scaling list is + /// not used for the scaling process for transform coefficients. + pub scaling_list_enabled_flag: bool, + /* if scaling_list_enabled_flag */ + /// When true, specifies that the scaling_list_data( ) syntax structure is + /// present in the SPS. When false, specifies that the scaling_list_data( ) + /// syntax structure is not present in the SPS. + pub scaling_list_data_present_flag: bool, + /// The scaling_list_data() syntax data. + pub scaling_list: ScalingLists, + /// When true, specifies that asymmetric motion partitions, i.e., PartMode + /// equal to PART_2NxnU, PART_2NxnD, PART_nLx2N or PART_nRx2N, may be used + /// in CTBs. When false, specifies that asymmetric motion partitions cannot + /// be used in CTBs. + pub amp_enabled_flag: bool, + /// When true, specifies that the sample adaptive offset process is applied + /// to the reconstructed picture after the deblocking filter process. When + /// false, specifies that the sample adaptive offset process is not applied + /// to the reconstructed picture after the deblocking filter process. + pub sample_adaptive_offset_enabled_flag: bool, + /// When false, specifies that PCM-related syntax + /// (pcm_sample_bit_depth_luma_minus1, pcm_sample_ bit_depth_chroma_minus1, + /// log2_min_pcm_luma_coding_block_size_minus3, log2_diff_max_min_pcm_luma_ + /// coding_block_size, pcm_loop_filter_disabled_flag, pcm_flag, + /// pcm_alignment_zero_bit syntax elements and pcm_sample( ) syntax + /// structure) is not present in the CVS. + pub pcm_enabled_flag: bool, + + /* if pcm_enabled_flag */ + pub pcm_sample_bit_depth_luma_minus1: u8, + /// Specifies the number of bits used to represent each of PCM sample values + /// of the luma component. + pub pcm_sample_bit_depth_chroma_minus1: u8, + /// Specifies the number of bits used to represent each of PCM sample values + /// of the chroma components. + pub log2_min_pcm_luma_coding_block_size_minus3: u8, + /// Specifies the difference between the maximum and minimum size of coding + /// blocks with pcm_flag equal to true. + pub log2_diff_max_min_pcm_luma_coding_block_size: u8, + /// Specifies whether the loop filter process is disabled on reconstructed + /// samples in a coding unit with pcm_flag equal to true as follows: + /// + /// – If pcm_loop_filter_disabled_flag is set, the deblocking filter and + /// sample adaptive offset filter processes on the reconstructed samples in + /// a coding unit with pcm_flag set are disabled. + /// + /// – Otherwise (pcm_loop_filter_disabled_flag value is not set), the + /// deblocking filter and sample adaptive offset filter processes on the + /// reconstructed samples in a coding unit with pcm_flag set are not + /// disabled. + pub pcm_loop_filter_disabled_flag: bool, + /// Specifies the number of st_ref_pic_set( ) syntax structures included in + /// the SPS. + pub num_short_term_ref_pic_sets: u8, + /// the st_ref_pic_set() data. + pub short_term_ref_pic_set: Vec, + /// If unset, specifies that no long-term reference picture is used for + /// inter prediction of any coded picture in the CVS. + /// If set, specifies that long-term reference pictures may be used for + /// inter prediction of one or more coded pictures in the CVS. + pub long_term_ref_pics_present_flag: bool, + + /* if long_term_ref_pics_present_flag */ + /// Specifies the number of candidate long-term reference pictures that are + /// specified in the SPS. + pub num_long_term_ref_pics_sps: u8, + /// `lt_ref_pic_poc_lsb_sps[ i ]` specifies the picture order count modulo + /// MaxPicOrderCntLsb of the i-th candidate long-term reference picture + /// specified in the SPS. + pub lt_ref_pic_poc_lsb_sps: [u32; MAX_LONG_TERM_REF_PIC_SETS], + /// `used_by_curr_pic_lt_sps_flag[ i ]` equal to false specifies that the i-th + /// candidate long-term reference picture specified in the SPS is not used + /// for reference by a picture that includes in its long-term reference + /// picture set (RPS) the i-th candidate long-term reference picture + /// specified in the SPS. + pub used_by_curr_pic_lt_sps_flag: [bool; MAX_LONG_TERM_REF_PIC_SETS], + /// When set, specifies that slice_temporal_mvp_enabled_flag is present in + /// the slice headers of non-IDR pictures in the CVS. When not set, + /// specifies that slice_temporal_mvp_enabled_flag is not present in slice + /// headers and that temporal motion vector predictors are not used in the + /// CVS. + pub temporal_mvp_enabled_flag: bool, + /// When set, specifies that bi-linear interpolation is conditionally used + /// in the intraprediction filtering process in the CVS as specified in + /// clause 8.4.4.2.3. + pub strong_intra_smoothing_enabled_flag: bool, + /// When set, specifies that the vui_parameters( ) syntax structure as + /// specified in Annex E is present. When not set, specifies that the + /// vui_parameters( ) syntax structure as specified in Annex E is not + /// present. + pub vui_parameters_present_flag: bool, + /// The vui_parameters() data. + pub vui_parameters: VuiParams, + /// When set, specifies that the syntax elements sps_range_extension_flag, + /// sps_multilayer_extension_flag, sps_3d_extension_flag, + /// sps_scc_extension_flag, and sps_extension_4bits are present in the SPS + /// RBSP syntax structure. When not set, specifies that these syntax + /// elements are not present. + pub extension_present_flag: bool, + + pub range_extension_flag: bool, + /// The sps_range_extension() data. + pub range_extension: SpsRangeExtension, + /// When set, specifies that the sps_scc_extension( ) syntax structure is + /// present in the SPS RBSP syntax structure. When not set, specifies that + /// this syntax structure is not present + pub scc_extension_flag: bool, + /// The sps_scc_extension() data. + pub scc_extension: SpsSccExtension, + + // Internal H265 variables. Computed from the bitstream. + /// Equivalent to MinCbLog2SizeY in the specification. + pub min_cb_log2_size_y: u32, + /// Equivalent to CtbLog2SizeY in the specification. + pub ctb_log2_size_y: u32, + /// Equivalent to CtbSizeY in the specification. + pub ctb_size_y: u32, + /// Equivalent to PicHeightInCtbsY in the specification. + pub pic_height_in_ctbs_y: u32, + /// Equivalent to PicWidthInCtbsY in the specification. + pub pic_width_in_ctbs_y: u32, + /// Equivalent to PicSizeInCtbsY in the specification. + pub pic_size_in_ctbs_y: u32, + /// Equivalent to ChromaArrayType in the specification. + pub chroma_array_type: u8, + /// Equivalent to WpOffsetHalfRangeY in the specification. + pub wp_offset_half_range_y: u32, + /// Equivalent to WpOffsetHalfRangeC in the specification. + pub wp_offset_half_range_c: u32, + /// Equivalent to MaxTbLog2SizeY in the specification. + pub max_tb_log2_size_y: u32, + /// Equivalent to PicSizeInSamplesY in the specification. + pub pic_size_in_samples_y: u32, + + /// The VPS referenced by this SPS, if any. + pub vps: Option>, +} + +impl Sps { + pub fn max_dpb_size(&self) -> usize { + let max_luma_ps = self.profile_tier_level.max_luma_ps(); + let max_dpb_pic_buf = self.profile_tier_level.max_dpb_pic_buf(); + + // Equation A-2 + let max = if self.pic_size_in_samples_y <= (max_luma_ps >> 2) { + std::cmp::min(4 * max_dpb_pic_buf, 16) + } else if self.pic_size_in_samples_y <= (max_luma_ps >> 1) { + std::cmp::min(2 * max_dpb_pic_buf, 16) + } else if self.pic_size_in_samples_y <= ((3 * max_luma_ps) >> 2) { + std::cmp::min(4 * max_dpb_pic_buf / 3, 16) + } else { + max_dpb_pic_buf + }; + + max as usize + } + + pub fn width(&self) -> u16 { + self.pic_width_in_luma_samples + } + + pub fn height(&self) -> u16 { + self.pic_height_in_luma_samples + } + + pub fn visible_rectangle(&self) -> Rect { + // From the specification: + // NOTE 3 – The conformance cropping window offset parameters are + // only applied at the output. All internal decoding processes are + // applied to the uncropped picture size. + if !self.conformance_window_flag { + return Rect { + min: Point { x: 0, y: 0 }, + max: Point { x: u32::from(self.width()), y: u32::from(self.height()) }, + }; + } + const SUB_HEIGHT_C: [u32; 5] = [1, 2, 1, 1, 1]; + const SUB_WIDTH_C: [u32; 5] = [1, 2, 2, 1, 1]; + + let crop_unit_y = SUB_HEIGHT_C[usize::from(self.chroma_array_type)]; + let crop_unit_x = SUB_WIDTH_C[usize::from(self.chroma_array_type)]; + let crop_left = crop_unit_x * self.conf_win_left_offset; + let crop_right = crop_unit_x * self.conf_win_right_offset; + let crop_top = crop_unit_y * self.conf_win_top_offset; + let crop_bottom = crop_unit_y * self.conf_win_bottom_offset; + + Rect { + min: Point { x: crop_left, y: crop_top }, + max: Point { + x: u32::from(self.width()) - crop_left - crop_right, + y: u32::from(self.height()) - crop_top - crop_bottom, + }, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PpsSccExtension { + /// When set, specifies that a picture referring to the PPS may be included + /// in a reference picture list of a slice of the picture itself. If not + /// set, specifies that a picture referring to the PPS is never included in + /// a reference picture list of a slice of the picture itself. + pub curr_pic_ref_enabled_flag: bool, + /// When set, specifies that an adaptive colour transform may be applied to + /// the residual in the decoding process. When not set, specifies that + /// adaptive colour transform is not applied to the residual. + pub residual_adaptive_colour_transform_enabled_flag: bool, + /// When set, specifies that slice_act_y_qp_offset, slice_act_cb_qp_offset, + /// slice_act_cr_qp_offset are present in the slice header. When not set, + /// specifies that slice_act_y_qp_offset, slice_act_cb_qp_offset, + /// slice_act_cr_qp_offset are not present in the slice header. + pub slice_act_qp_offsets_present_flag: bool, + /// See the specificartion for more details. + pub act_y_qp_offset_plus5: i8, + /// See the specificartion for more details. + pub act_cb_qp_offset_plus5: i8, + /// See the specificartion for more details. + pub act_cr_qp_offset_plus3: i8, + /// When set, specifies that the palette predictor initializers used for the + /// pictures referring to the PPS are derived based on the palette predictor + /// initializers specified by the PPS. If not set, specifies that the + /// palette predictor initializers used for the pictures referring to the + /// PPS are inferred to be equal to those specified by the active SPS. + pub palette_predictor_initializers_present_flag: bool, + /// Specifies the number of entries in the picture palette predictor + /// initializer. + pub num_palette_predictor_initializers: u8, + /// When set, specifies that the pictures that refer to this PPS are + /// monochrome. If not set, specifies that the pictures that refer to this + /// PPS have multiple components. + pub monochrome_palette_flag: bool, + /// luma_bit_depth_entry_minus8 plus 8 specifies the bit depth of the luma + /// component of the entries of the palette predictor initializer. + pub luma_bit_depth_entry_minus8: u8, + /// chroma_bit_depth_entry_minus8 plus 8 specifies the bit depth of the + /// chroma components of the entries of the palette predictor initializer. + pub chroma_bit_depth_entry_minus8: u8, + /// `pps_palette_predictor_initializer[ comp ][ i ]` specifies the value of + /// the comp-th component of the i-th palette entry in the PPS that is used + /// to initialize the array PredictorPaletteEntries. + pub palette_predictor_initializer: [[u8; 128]; 3], +} + +impl Default for PpsSccExtension { + fn default() -> Self { + Self { + curr_pic_ref_enabled_flag: Default::default(), + residual_adaptive_colour_transform_enabled_flag: Default::default(), + slice_act_qp_offsets_present_flag: Default::default(), + act_y_qp_offset_plus5: Default::default(), + act_cb_qp_offset_plus5: Default::default(), + act_cr_qp_offset_plus3: Default::default(), + palette_predictor_initializers_present_flag: Default::default(), + num_palette_predictor_initializers: Default::default(), + monochrome_palette_flag: Default::default(), + luma_bit_depth_entry_minus8: Default::default(), + chroma_bit_depth_entry_minus8: Default::default(), + palette_predictor_initializer: [[0; 128]; 3], + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct PpsRangeExtension { + /// log2_max_transform_skip_block_size_minus2 plus 2 specifies the maximum + /// transform block size for which transform_skip_flag may be present in + /// coded pictures referring to the PPS. When not present, the value of + /// log2_max_transform_skip_block_size_minus2 is inferred to be equal to 0. + /// When present, the value of log2_max_transform_skip_block_size_minus2 + /// shall be less than or equal to MaxTbLog2SizeY − 2. + pub log2_max_transform_skip_block_size_minus2: u32, + /// When set, specifies that log2_res_scale_abs_plus1 and + /// res_scale_sign_flag may be present in the transform unit syntax for + /// pictures referring to the PPS. When not set, specifies that + /// log2_res_scale_abs_plus1 and res_scale_sign_flag are not present for + /// pictures referring to the PPS. + pub cross_component_prediction_enabled_flag: bool, + /// When set, specifies that the cu_chroma_qp_offset_flag may be present in + /// the transform unit syntax. When not set, specifies that the + /// cu_chroma_qp_offset_flag is not present in the transform unit syntax. + pub chroma_qp_offset_list_enabled_flag: bool, + /// Specifies the difference between the luma CTB size and the minimum luma + /// coding block size of coding units that convey cu_chroma_qp_offset_flag. + pub diff_cu_chroma_qp_offset_depth: u32, + /// chroma_qp_offset_list_len_minus1 plus 1 specifies the number of + /// `cb_qp_offset_list[ i ]` and `cr_qp_offset_list[ i ]` syntax elements that + /// are present in the PPS. + pub chroma_qp_offset_list_len_minus1: u32, + /// Specify offsets used in the derivation of Qp′Cb and Qp′Cr, respectively. + pub cb_qp_offset_list: [i32; 6], + /// Specify offsets used in the derivation of Qp′Cb and Qp′Cr, respectively. + pub cr_qp_offset_list: [i32; 6], + /// The base 2 logarithm of the scaling parameter that is used to scale + /// sample adaptive offset (SAO) offset values for luma samples. + pub log2_sao_offset_scale_luma: u32, + /// The base 2 logarithm of the scaling parameter that is used to scale SAO + /// offset values for chroma samples. + pub log2_sao_offset_scale_chroma: u32, +} + +/// A H.265 Picture Parameter Set. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Pps { + /// Identifies the PPS for reference by other syntax elements. + pub pic_parameter_set_id: u8, + /// Specifies the value of sps_seq_parameter_set_id for the active SPS. + pub seq_parameter_set_id: u8, + /// When set, specifies the presence of the syntax element + /// dependent_slice_segment_flag in the slice segment headers for coded + /// pictures referring to the PPS. When not set, specifies the absence of + /// the syntax element dependent_slice_segment_flag in the slice segment + /// headers for coded pictures referring to the PPS. + pub dependent_slice_segments_enabled_flag: bool, + /// When set, indicates that the pic_output_flag syntax element is present + /// in the associated slice headers. When not set, indicates that the + /// pic_output_flag syntax element is not present in the associated slice + /// headers. + pub output_flag_present_flag: bool, + /// Specifies the number of extra slice header bits that are present in the + /// slice header RBSP for coded pictures referring to the PPS. + pub num_extra_slice_header_bits: u8, + /// When not set, specifies that sign bit hiding is disabled. Whens set, + /// specifies that sign bit hiding is enabled. + pub sign_data_hiding_enabled_flag: bool, + /// When set, specifies that cabac_init_flag is present in slice headers + /// referring to the PPS. When not set, specifies that cabac_init_flag is + /// not present in slice headers referring to the PPS. + pub cabac_init_present_flag: bool, + /// Specifies the inferred value of num_ref_idx_l0_active_minus1 for P and B + /// slices with num_ref_idx_active_override_flag not set. + pub num_ref_idx_l0_default_active_minus1: u8, + /// Specifies the inferred value of num_ref_idx_l1_active_minus1 for B + /// slices with num_ref_idx_active_override_flag not set. + pub num_ref_idx_l1_default_active_minus1: u8, + /// init_qp_minus26 plus 26 specifies the initial value of SliceQpY for each + /// slice referring to the PPS. The initial value of SliceQpY is modified at + /// the slice segment layer when a non-zero value of slice_qp_delta is + /// decoded. + pub init_qp_minus26: i8, + /// When not set, specifies that intra prediction allows usage of residual + /// data and decoded samples of neighbouring coding blocks coded using + /// either intra or inter prediction modes. When set, specifies constrained + /// intra prediction, in which case intra prediction only uses residual data + /// and decoded samples from neighbouring coding blocks coded using intra + /// prediction modes. + pub constrained_intra_pred_flag: bool, + /// When set, specifies that transform_skip_flag may be present in the + /// residual coding syntax. When not set, specifies that transform_skip_flag + /// is not present in the residual coding syntax. + pub transform_skip_enabled_flag: bool, + /// When set, specifies that the diff_cu_qp_delta_depth syntax element is + /// present in the PPS and that cu_qp_delta_abs may be present in the + /// transform unit syntax and the palette syntax. When not set, specifies + /// that the diff_cu_qp_delta_depth syntax element is not present in the PPS + /// and that cu_qp_delta_abs is not present in the transform unit syntax and + /// the palette syntax. + pub cu_qp_delta_enabled_flag: bool, + + /*if cu_qp_delta_enabled_flag */ + /// Specifies the difference between the luma CTB size and the minimum luma + /// coding block size of coding units that convey cu_qp_delta_abs and + /// cu_qp_delta_sign_flag. + pub diff_cu_qp_delta_depth: u8, + /// Specifies the offsets to the luma quantization parameter Qp′Y used for + /// deriving Qp′Cb and Qp′Cr, respectively. + pub cb_qp_offset: i8, + /// Specifies the offsets to the luma quantization parameter Qp′Y used for + /// deriving Qp′Cb and Qp′Cr, respectively. + pub cr_qp_offset: i8, + /// When set, indicates that the slice_cb_qp_offset and slice_cr_qp_offset + /// syntax elements are present in the associated slice headers. When not + /// set, indicates that these syntax elements are not present in the + /// associated slice headers. When ChromaArrayType is equal to 0, + /// pps_slice_chroma_qp_offsets_present_flag shall be equal to 0 + pub slice_chroma_qp_offsets_present_flag: bool, + /// When not set, specifies that weighted prediction is not applied to P + /// slices. When set, specifies that weighted prediction is applied to P + /// slices. + pub weighted_pred_flag: bool, + /// When not set, specifies that the default weighted prediction is applied + /// to B slices. When set, specifies that weighted prediction is applied to + /// B slices. + pub weighted_bipred_flag: bool, + /// When set, specifies that `cu_transquant_bypass_flag` is present, When + /// not set, specifies that `cu_transquant_bypass_flag` is not present. + pub transquant_bypass_enabled_flag: bool, + /// When set, specifies that there is more than one tile in each picture + /// referring to the PPS. When not set, specifies that there is only one + /// tile in each picture referring to the PPS. + pub tiles_enabled_flag: bool, + /// When set, specifies that a specific synchronization process for context + /// variables, and when applicable, Rice parameter initialization states and + /// palette predictor variables, is invoked before decoding the CTU which + /// includes the first CTB of a row of CTBs in each tile in each picture + /// referring to the PPS, and a specific storage process for context + /// variables, and when applicable, Rice parameter initialization states and + /// palette predictor variables, is invoked after decoding the CTU which + /// includes the second CTB of a row of CTBs in each tile in each picture + /// referring to the PPS. When not set, specifies that no specific + /// synchronization process for context variables, and when applicable, Rice + /// parameter initialization states and palette predictor variables, is + /// required to be invoked before decoding the CTU which includes the first + /// CTB of a row of CTBs in each tile in each picture referring to the PPS, + /// and no specific storage process for context variables, and when + /// applicable, Rice parameter initialization states and palette predictor + /// variables, is required to be invoked after decoding the CTU which + /// includes the second CTB of a row of CTBs in each tile in each picture + /// referring to the PPS. + pub entropy_coding_sync_enabled_flag: bool, + /// num_tile_columns_minus1 plus 1 specifies the number of tile columns + /// partitioning the picture. + pub num_tile_columns_minus1: u8, + /// num_tile_rows_minus1 plus 1 specifies the number of tile rows + /// partitioning the picture. + pub num_tile_rows_minus1: u8, + /// When set, specifies that tile column boundaries and likewise tile row + /// boundaries are distributed uniformly across the picture. When not set, + /// specifies that tile column boundaries and likewise tile row boundaries + /// are not distributed uniformly across the picture but signalled + /// explicitly using the syntax elements `column_width_minus1[ i ]` and + /// `row_height_minus1[ i ]`. + pub uniform_spacing_flag: bool, + /// `column_width_minus1[ i ]` plus 1 specifies the width of the i-th tile + /// column in units of CTBs. + pub column_width_minus1: [u32; 19], + /// `row_height_minus1[ i ]` plus 1 specifies the height of the i-th tile row + /// in units of CTBs. + pub row_height_minus1: [u32; 21], + /// When set, specifies that in-loop filtering operations may be performed + /// across tile boundaries in pictures referring to the PPS. When not set, + /// specifies that in-loop filtering operations are not performed across + /// tile boundaries in pictures referring to the PPS. The in-loop filtering + /// operations include the deblocking filter and sample adaptive offset + /// filter operations. + pub loop_filter_across_tiles_enabled_flag: bool, + /// When set, specifies that in-loop filtering operations may be performed + /// across left and upper boundaries of slices referring to the PPS. When + /// not set, specifies that in-loop filtering operations are not performed + /// across left and upper boundaries of slices referring to the PPS. The in- + /// loop filtering operations include the deblocking filter and sample + /// adaptive offset filter operations. + pub loop_filter_across_slices_enabled_flag: bool, + /// When set, specifies the presence of deblocking filter control syntax + /// elements in the PPS. When not set, specifies the absence of deblocking + /// filter control syntax elements in the PPS. + pub deblocking_filter_control_present_flag: bool, + /// When set, specifies the presence of deblocking_filter_override_flag in + /// the slice headers for pictures referring to the PPS. When not set, + /// specifies the absence of deblocking_filter_override_flag in the slice + /// headers for pictures referring to the PPS. + pub deblocking_filter_override_enabled_flag: bool, + /// When set, specifies that the operation of deblocking filter is not + /// applied for slices referring to the PPS in which + /// slice_deblocking_filter_disabled_flag is not present. When not set, + /// specifies that the operation of the deblocking filter is applied for + /// slices referring to the PPS in which + /// slice_deblocking_filter_disabled_flag is not present. + pub deblocking_filter_disabled_flag: bool, + /// Specify the default deblocking parameter offsets for β and tC (divided + /// by 2) that are applied for slices referring to the PPS, unless the + /// default deblocking parameter offsets are overridden by the deblocking + /// parameter offsets present in the slice headers of the slices referring + /// to the PPS. + pub beta_offset_div2: i8, + /// Specify the default deblocking parameter offsets for β and tC (divided + /// by 2) that are applied for slices referring to the PPS, unless the + /// default deblocking parameter offsets are overridden by the deblocking + /// parameter offsets present in the slice headers of the slices referring + /// to the PPS. + pub tc_offset_div2: i8, + /// When set, specifies that the scaling list data used for the pictures + /// referring to the PPS are derived based on the scaling lists specified by + /// the active SPS and the scaling lists specified by the PPS. + /// pps_scaling_list_data_present_flag equal to 0 specifies that the scaling + /// list data used for the pictures referring to the PPS are inferred to be + /// equal to those specified by the active SPS. + pub scaling_list_data_present_flag: bool, + /// The scaling list data. + pub scaling_list: ScalingLists, + /// When set, specifies that the syntax structure + /// ref_pic_lists_modification( ) is present in the slice segment header. + /// When not set, specifies that the syntax structure + /// ref_pic_lists_modification( ) is not present in the slice segment header + pub lists_modification_present_flag: bool, + /// log2_parallel_merge_level_minus2 plus 2 specifies the value of the + /// variable Log2ParMrgLevel, which is used in the derivation process for + /// luma motion vectors for merge mode as specified in clause 8.5.3.2.2 and + /// the derivation process for spatial merging candidates as specified in + /// clause 8.5.3.2.3. + pub log2_parallel_merge_level_minus2: u8, + /// When not set, specifies that no slice segment header extension syntax + /// elements are present in the slice segment headers for coded pictures + /// referring to the PPS. When set, specifies that slice segment header + /// extension syntax elements are present in the slice segment headers for + /// coded pictures referring to the PPS. + pub slice_segment_header_extension_present_flag: bool, + /// When set, specifies that the syntax elements pps_range_extension_flag, + /// pps_multilayer_extension_flag, pps_3d_extension_flag, + /// pps_scc_extension_flag, and pps_extension_4bits are present in the + /// picture parameter set RBSP syntax structure. When not set, specifies + /// that these syntax elements are not present. + pub extension_present_flag: bool, + /// When setspecifies that the pps_range_extension( ) syntax structure is + /// present in the PPS RBSP syntax structure. When not set, specifies that + /// this syntax structure is not present. + pub range_extension_flag: bool, + /// The range extension data. + pub range_extension: PpsRangeExtension, + + pub scc_extension_flag: bool, + /// The SCC extension data. + pub scc_extension: PpsSccExtension, + + // Internal variables. + /// Equivalent to QpBdOffsetY in the specification. + pub qp_bd_offset_y: u32, + + /// The SPS referenced by this PPS. + pub sps: Rc, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ScalingLists { + /// plus 8 specifies the value of the variable `ScalingFactor[ 2 ][ matrixId + /// ] [ 0 ][ 0 ]` for the scaling list for the 16x16 size. + pub scaling_list_dc_coef_minus8_16x16: [i16; 6], + /// plus 8 specifies the value of the variable `ScalingFactor[ 3 ][ matrixId + /// ][ 0 ][ 0 ]` for the scaling list for the 32x32 size. + pub scaling_list_dc_coef_minus8_32x32: [i16; 6], + /// The 4x4 scaling list. + pub scaling_list_4x4: [[u8; 16]; 6], + /// The 8x8 scaling list. + pub scaling_list_8x8: [[u8; 64]; 6], + /// The 16x16 scaling list. + pub scaling_list_16x16: [[u8; 64]; 6], + /// The 32x32 scaling list. + pub scaling_list_32x32: [[u8; 64]; 6], +} + +impl Default for ScalingLists { + fn default() -> Self { + Self { + scaling_list_dc_coef_minus8_16x16: Default::default(), + scaling_list_dc_coef_minus8_32x32: Default::default(), + scaling_list_4x4: Default::default(), + scaling_list_8x8: [[0; 64]; 6], + scaling_list_16x16: [[0; 64]; 6], + scaling_list_32x32: [[0; 64]; 6], + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct RefPicListModification { + /// Whenset, indicates that reference picture list 0 is specified explicitly + /// by a list of `list_entry_l0[ i ]` values. When not set, indicates that + /// reference picture list 0 is determined implicitly. + pub ref_pic_list_modification_flag_l0: bool, + /// `list_entry_l0[ i ]` specifies the index of the reference picture in + /// RefPicListTemp0 to be placed at the current position of reference + /// picture list 0. + pub list_entry_l0: Vec, + /// Whenset, indicates that reference picture list 1 is specified explicitly + /// by a list of `list_entry_l1[ i ]` values. When not set, indicates that + /// reference picture list 1 is determined implicitly. + pub ref_pic_list_modification_flag_l1: bool, + /// `list_entry_l1[ i ]` specifies the index of the reference picture in + /// RefPicListTemp1 to be placed at the current position of reference + /// picture list 1. + pub list_entry_l1: Vec, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct PredWeightTable { + /// The base 2 logarithm of the denominator for all luma weighting factors. + pub luma_log2_weight_denom: u8, + /// The difference of the base 2 logarithm of the denominator for all chroma + /// weighting factors. + pub delta_chroma_log2_weight_denom: i8, + /// `luma_weight_l0_flag[ i ]` set specifies that weighting factors for the + /// luma component of list 0 prediction using `RefPicList0[ i ]` are present. + /// `luma_weight_l0_flag[ i ]` not set specifies that these weighting factors + /// are not present. + pub luma_weight_l0_flag: [bool; 15], + /// `chroma_weight_l0_flag[ i ]` set specifies that weighting factors for the + /// chroma prediction values of list 0 prediction using `RefPicList0[ i ]` are + /// present. `chroma_weight_l0_flag[ i ]` not set specifies that these + /// weighting factors are not present. + pub chroma_weight_l0_flag: [bool; 15], + /// `delta_luma_weight_l0[ i ]` is the difference of the weighting factor + /// applied to the luma prediction value for list 0 prediction using + /// `RefPicList0[ i ]`. + pub delta_luma_weight_l0: [i8; 15], + /// `luma_offset_l0[ i ]` is the additive offset applied to the luma + /// prediction value for list 0 prediction using `RefPicList0[ i ]`. + pub luma_offset_l0: [i8; 15], + /// `delta_chroma_weight_l0[ i ][ j ]` is the difference of the weighting + /// factor applied to the chroma prediction values for list 0 prediction + /// using `RefPicList0[ i ]` with j equal to 0 for Cb and j equal to 1 for Cr. + pub delta_chroma_weight_l0: [[i8; 2]; 15], + /// `delta_chroma_offset_l0[ i ][ j ]` is the difference of the additive + /// offset applied to the chroma prediction values for list 0 prediction + /// using `RefPicList0[ i ]` with j equal to 0 for Cb and j equal to 1 for Cr. + pub delta_chroma_offset_l0: [[i16; 2]; 15], + + // `luma_weight_l1_flag[ i ]`, `chroma_weight_l1_flag[ i ]`, + // `delta_luma_weight_l1[ i ]`, `luma_offset_l1[ i ]`, delta_chroma_weight_l1[ i + // `][ j ]` and `delta_chroma_offset_l1[ i ]`[ j ] have the same + // `semanticsasluma_weight_l0_flag[ i ]`, `chroma_weight_l0_flag[ i ]`, + // `delta_luma_weight_l0[ i ]`, `luma_offset_l0[ i ]`, `delta_chroma_weight_l0[ i + // ][ j ]` and `delta_chroma_offset_l0[ i ][ j ]`, respectively, with `l0`, `L0`, + // `list 0` and `List0` replaced by `l1`, `L1`, `list 1` and `List1`, respectively. + pub luma_weight_l1_flag: [bool; 15], + pub chroma_weight_l1_flag: [bool; 15], + pub delta_luma_weight_l1: [i8; 15], + pub luma_offset_l1: [i8; 15], + + pub delta_chroma_weight_l1: [[i8; 2]; 15], + pub delta_chroma_offset_l1: [[i16; 2]; 15], + + // Calculated. + /// Same as ChromaLog2WeightDenom in the specification. + pub chroma_log2_weight_denom: u8, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ShortTermRefPicSet { + /// When set, specifies that the stRpsIdx-th candidate short-term RPS is + /// predicted from another candidate short-term RPS, which is referred to as + /// the source candidate short-term RPS. + pub inter_ref_pic_set_prediction_flag: bool, + /// delta_idx_minus1 plus 1 specifies the difference between the value of + /// stRpsIdx and the index, into the list of the candidate short-term RPSs + /// specified in the SPS, of the source candidate short-term RPS. + pub delta_idx_minus1: u8, + /// delta_rps_sign and abs_delta_rps_minus1 together specify the value of + /// the variable deltaRps. + pub delta_rps_sign: bool, + /// delta_rps_sign and abs_delta_rps_minus1 together specify the value of + /// the variable deltaRps. + pub abs_delta_rps_minus1: u16, + /// specifies the number of entries in the stRpsIdx-th candidate short-term + /// RPS that have picture order count values less than the picture order + /// count value of the current picture. + pub num_negative_pics: u8, + /// specifies the number of entries in the stRpsIdx-th candidate short-term + /// RPS that have picture order count values greater than the picture order + /// count value of the current picture. + pub num_positive_pics: u8, + /// Same as UsedByCurrPicS0 in the specification. + pub used_by_curr_pic_s0: [bool; MAX_SHORT_TERM_REF_PIC_SETS], + /// Same as UsedByCurrPicS1 in the specification. + pub used_by_curr_pic_s1: [bool; MAX_SHORT_TERM_REF_PIC_SETS], + /// Same as DeltaPocS0 in the specification. + pub delta_poc_s0: [i32; MAX_SHORT_TERM_REF_PIC_SETS], + /// Same as DeltaPocS1 in the specification. + pub delta_poc_s1: [i32; MAX_SHORT_TERM_REF_PIC_SETS], + /// Same as NumDeltaPocs in the specification. + pub num_delta_pocs: u32, +} + +impl Default for ShortTermRefPicSet { + fn default() -> Self { + Self { + inter_ref_pic_set_prediction_flag: Default::default(), + delta_idx_minus1: Default::default(), + delta_rps_sign: Default::default(), + abs_delta_rps_minus1: Default::default(), + num_negative_pics: Default::default(), + num_positive_pics: Default::default(), + used_by_curr_pic_s0: [false; MAX_SHORT_TERM_REF_PIC_SETS], + used_by_curr_pic_s1: [false; MAX_SHORT_TERM_REF_PIC_SETS], + delta_poc_s0: [0; MAX_SHORT_TERM_REF_PIC_SETS], + delta_poc_s1: [0; MAX_SHORT_TERM_REF_PIC_SETS], + num_delta_pocs: Default::default(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// See table 7-7 in the specification. +pub enum SliceType { + B = 0, + P = 1, + I = 2, +} + +impl TryFrom for SliceType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(SliceType::B), + 1 => Ok(SliceType::P), + 2 => Ok(SliceType::I), + _ => Err(format!("Invalid SliceType {}", value)), + } + } +} + +impl SliceType { + /// Whether this is a P slice. See table 7-7 in the specification. + pub fn is_p(&self) -> bool { + matches!(self, SliceType::P) + } + + /// Whether this is a B slice. See table 7-7 in the specification. + pub fn is_b(&self) -> bool { + matches!(self, SliceType::B) + } + + /// Whether this is an I slice. See table 7-7 in the specification. + pub fn is_i(&self) -> bool { + matches!(self, SliceType::I) + } +} + +impl Default for SliceType { + fn default() -> Self { + Self::P + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SliceHeader { + /// When set, specifies that the slice segment is the first slice segment of + /// the picture in decoding order. When not set, specifies that the slice + /// segment is not the first slice segment of the picture in decoding order. + pub first_slice_segment_in_pic_flag: bool, + /// Affects the output of previously-decoded pictures in the decoded picture + /// buffer after the decoding of an IDR or a BLA picture that is not the + /// first picture in the bitstream as specified in Annex C. + pub no_output_of_prior_pics_flag: bool, + /// Specifies the value of pps_pic_parameter_set_id for the PPS in use. + pub pic_parameter_set_id: u8, + /// When set, specifies that the value of each slice segment header syntax + /// element that is not present is inferred to be equal to the value of the + /// corresponding slice segment header syntax element in the slice header. + pub dependent_slice_segment_flag: bool, + /// Specifies the address of the first CTB in the slice segment, in CTB + /// raster scan of a picture. + pub segment_address: u32, + /// Specifies the coding type of the slice according to Table 7-7. + pub type_: SliceType, + /// Affects the decoded picture output and removal processes as specified in + /// Annex C. + pub pic_output_flag: bool, + /// Specifies the colour plane associated with the current slice RBSP when + /// separate_colour_plane_flag is set. The value of colour_plane_id shall be + /// in the range of 0 to 2, inclusive. colour_plane_id values 0, 1 and 2 + /// correspond to the Y, Cb and Cr planes, respectively. + pub colour_plane_id: u8, + /// Specifies the picture order count modulo MaxPicOrderCntLsb for the + /// current picture. The length of the slice_pic_order_cnt_lsb syntax + /// element is log2_max_pic_order_cnt_lsb_minus4 + 4 bits. + pub pic_order_cnt_lsb: u16, + /// When set, specifies that the short-term RPS of the current picture is + /// derived based on one of the st_ref_pic_set( ) syntax structures in the + /// active SPS that is identified by the syntax element + /// short_term_ref_pic_set_idx in the slice header. When not set, specifies + /// that the short-term RPS of the current picture is derived based on the + /// st_ref_pic_set( ) syntax structure that is directly included in the + /// slice headers of the current picture. + pub short_term_ref_pic_set_sps_flag: bool, + /// The st_ref_pic_set() data. + pub short_term_ref_pic_set: ShortTermRefPicSet, + /// Specifies the index, into the list of the st_ref_pic_set( ) syntax + /// structures included in the active SPS, of the st_ref_pic_set( ) syntax + /// structure that is used for derivation of the short-term RPS of the + /// current picture. + pub short_term_ref_pic_set_idx: u8, + /// Specifies the number of entries in the long-term RPS of the current + /// picture that are derived based on the candidate long-term reference + /// pictures specified in the active SPS. + pub num_long_term_sps: u8, + /// Specifies the number of entries in the long-term RPS of the current + /// picture that are directly signalled in the slice header. + pub num_long_term_pics: u8, + /// `lt_idx_sps[ i ]` specifies an index, into the list of candidate long-term + /// reference pictures specified in the active SPS, of the i-th entry in the + /// long-term RPS of the current picture. + pub lt_idx_sps: [u8; 16], + /// Same as PocLsbLt in the specification. + pub poc_lsb_lt: [u32; 16], + /// Same as UsedByCurrPicLt in the specification. + pub used_by_curr_pic_lt: [bool; 16], + /// When set, specifies that that `delta_poc_msb_cycle_lt[i]` is present. + pub delta_poc_msb_present_flag: [bool; 16], + /// Same as DeltaPocMsbCycleLt in the specification. + pub delta_poc_msb_cycle_lt: [u32; 16], + /// Specifies whether temporal motion vector predictors can be used for + /// inter prediction. If slice_temporal_mvp_enabled_flag is not set, the + /// syntax elements of the current picture shall be constrained such that no + /// temporal motion vector predictor is used in decoding of the current + /// picture. Otherwise (slice_temporal_mvp_enabled_flag is set), temporal + /// motion vector predictors may be used in decoding of the current picture. + pub temporal_mvp_enabled_flag: bool, + /// When set, specifies that SAO is enabled for the luma component in the + /// current slice; slice_sao_luma_flag not set specifies that SAO is + /// disabled for the luma component in the current slice. + pub sao_luma_flag: bool, + /// When set, specifies that SAO is enabled for the chroma component in the + /// current slice; When not set, specifies that SAO is disabled for the + /// chroma component in the current slice. + pub sao_chroma_flag: bool, + /// When set, specifies that the syntax element num_ref_idx_l0_active_minus1 + /// is present for P and B slices and that the syntax element + /// num_ref_idx_l1_active_minus1 is present for B slices. When not set, + /// specifies that the syntax elements num_ref_idx_l0_active_minus1 and + /// num_ref_idx_l1_active_minus1 are not present. + pub num_ref_idx_active_override_flag: bool, + /// Specifies the maximum reference index for + /// reference picture list 0 that may be used to decode the slice. + pub num_ref_idx_l0_active_minus1: u8, + /// Specifies the maximum reference index for reference picture list 1 that + /// may be used to decode the slice. + pub num_ref_idx_l1_active_minus1: u8, + /// The RefPicListModification data. + pub ref_pic_list_modification: RefPicListModification, + /// When set, indicates that the mvd_coding( x0, y0, 1 ) syntax structure is + /// not parsed and `MvdL1[ x0 ]`[ y0 `][ compIdx ]` is set equal to 0 for + /// compIdx = 0..1. When not set, indicates that the mvd_coding( x0, y0, 1 ) + /// syntax structure is parsed. + pub mvd_l1_zero_flag: bool, + /// Specifies the method for determining the initialization table used in + /// the initialization process for context variables. + pub cabac_init_flag: bool, + /// When set, specifies that the collocated picture used for temporal motion + /// vector prediction is derived from reference picture list 0. When not + /// set, specifies that the collocated picture used for temporal motion + /// vector prediction is derived from reference picture list 1. + pub collocated_from_l0_flag: bool, + /// Specifies the reference index of the collocated picture used for + /// temporal motion vector prediction. + pub collocated_ref_idx: u8, + /// The PredWeightTable data. + pub pred_weight_table: PredWeightTable, + /// Specifies the maximum number of merging motion vector prediction (MVP) + /// candidates supported in the slice subtracted from 5. + pub five_minus_max_num_merge_cand: u8, + /// Specifies that the resolution of motion vectors for inter prediction in + /// the current slice is integer. When not set, specifies + /// that the resolution of motion vectors for inter prediction in the + /// current slice that refer to pictures other than the current picture is + /// fractional with quarter-sample precision in units of luma samples. + pub use_integer_mv_flag: bool, + /// Specifies the initial value of QpY to be used for the coding blocks in + /// the slice until modified by the value of CuQpDeltaVal in the coding unit + /// layer. + pub qp_delta: i8, + /// Specifies a difference to be added to the value of pps_cb_qp_offset when + /// determining the value of the Qp′Cb quantization parameter. + pub cb_qp_offset: i8, + /// Specifies a difference to be added to the value of pps_cb_qr_offset when + /// determining the value of the Qp′Cr quantization parameter. + pub cr_qp_offset: i8, + /// Specifies offsets to the quantization parameter values qP derived in + /// clause 8.6.2 for luma, Cb, and Cr components, respectively. + pub slice_act_y_qp_offset: i8, + /// Specifies offsets to the quantization parameter values qP derived in + /// clause 8.6.2 for luma, Cb, and Cr components, respectively. + pub slice_act_cb_qp_offset: i8, + /// Specifies offsets to the quantization parameter values qP derived in + /// clause 8.6.2 for luma, Cb, and Cr components, respectively. + pub slice_act_cr_qp_offset: i8, + /// When set, specifies that the cu_chroma_qp_offset_flag may be present in + /// the transform unit syntax. When not set, specifies that the + /// cu_chroma_qp_offset_flag is not present in the transform unit syntax. + pub cu_chroma_qp_offset_enabled_flag: bool, + /// When set, specifies that deblocking parameters are present in the slice + /// header. When not set, specifies that deblocking parameters are not + /// present in the slice header. + pub deblocking_filter_override_flag: bool, + /// When set, specifies that the operation of the deblocking filter is not + /// applied for the current slice. When not set, specifies that the + /// operation of the deblocking filter is applied for the current slice. + pub deblocking_filter_disabled_flag: bool, + /// Specifies the deblocking parameter offsets for β and tC (divided by 2) + /// for the current slice. + pub beta_offset_div2: i8, + /// Specifies the deblocking parameter offsets for β and tC (divided by 2) + /// for the current slice. + pub tc_offset_div2: i8, + /// When set, specifies that in-loop filtering operations may be performed + /// across the left and upper boundaries of the current slice. When not + /// set, specifies that in-loop operations are not performed across left and + /// upper boundaries of the current slice. The in-loop filtering operations + /// include the deblocking filter and sample adaptive offset filter. + pub loop_filter_across_slices_enabled_flag: bool, + /// Specifies the number of `entry_point_offset_minus1[ i ]` syntax elements + /// in the slice header. + pub num_entry_point_offsets: u32, + /// offset_len_minus1 plus 1 specifies the length, in bits, of the + /// `entry_point_offset_minus1[ i ]` syntax elements. + pub offset_len_minus1: u8, + /// `entry_point_offset_minus1[ i ]` plus 1 specifies the i-th entry point + /// offset in bytes, and is represented by offset_len_minus1 plus 1 bits. + /// The slice segment data that follow the slice segment header consists of + /// num_entry_point_offsets + 1 subsets, with subset index values ranging + /// from 0 to num_entry_point_offsets, inclusive. See the specification for + /// more details. + pub entry_point_offset_minus1: [u32; 32], + /// Same as NumPicTotalCurr in the specification. + pub num_pic_total_curr: u32, + // Size of slice_header() in bits. + pub header_bit_size: u32, + // Number of emulation prevention bytes (EPB) in this slice_header(). + pub n_emulation_prevention_bytes: u32, + /// Same as CurrRpsIdx in the specification. + pub curr_rps_idx: u8, + /// Number of bits taken by st_ref_pic_set minus Emulation Prevention Bytes. + pub st_rps_bits: u32, +} + +impl Default for SliceHeader { + fn default() -> Self { + Self { + first_slice_segment_in_pic_flag: Default::default(), + no_output_of_prior_pics_flag: Default::default(), + pic_parameter_set_id: Default::default(), + dependent_slice_segment_flag: Default::default(), + segment_address: Default::default(), + type_: Default::default(), + pic_output_flag: true, + colour_plane_id: Default::default(), + pic_order_cnt_lsb: Default::default(), + short_term_ref_pic_set_sps_flag: Default::default(), + short_term_ref_pic_set: Default::default(), + short_term_ref_pic_set_idx: Default::default(), + num_long_term_sps: Default::default(), + num_long_term_pics: Default::default(), + lt_idx_sps: Default::default(), + poc_lsb_lt: Default::default(), + used_by_curr_pic_lt: Default::default(), + delta_poc_msb_present_flag: Default::default(), + delta_poc_msb_cycle_lt: Default::default(), + temporal_mvp_enabled_flag: Default::default(), + sao_luma_flag: Default::default(), + sao_chroma_flag: Default::default(), + num_ref_idx_active_override_flag: Default::default(), + num_ref_idx_l0_active_minus1: Default::default(), + num_ref_idx_l1_active_minus1: Default::default(), + ref_pic_list_modification: Default::default(), + mvd_l1_zero_flag: Default::default(), + cabac_init_flag: Default::default(), + collocated_from_l0_flag: true, + collocated_ref_idx: Default::default(), + pred_weight_table: Default::default(), + five_minus_max_num_merge_cand: Default::default(), + use_integer_mv_flag: Default::default(), + qp_delta: Default::default(), + cb_qp_offset: Default::default(), + cr_qp_offset: Default::default(), + slice_act_y_qp_offset: Default::default(), + slice_act_cb_qp_offset: Default::default(), + slice_act_cr_qp_offset: Default::default(), + cu_chroma_qp_offset_enabled_flag: Default::default(), + deblocking_filter_override_flag: Default::default(), + deblocking_filter_disabled_flag: Default::default(), + beta_offset_div2: Default::default(), + tc_offset_div2: Default::default(), + loop_filter_across_slices_enabled_flag: Default::default(), + num_entry_point_offsets: Default::default(), + offset_len_minus1: Default::default(), + entry_point_offset_minus1: Default::default(), + num_pic_total_curr: Default::default(), + header_bit_size: Default::default(), + n_emulation_prevention_bytes: Default::default(), + curr_rps_idx: Default::default(), + st_rps_bits: Default::default(), + } + } +} + +/// A H265 slice. An integer number of macroblocks or macroblock pairs ordered +/// consecutively in the raster scan within a particular slice group +pub struct Slice<'a> { + /// The slice header. + pub header: SliceHeader, + /// The NAL unit backing this slice. + pub nalu: Nalu<'a>, +} + +impl<'a> Slice<'a> { + /// Sets the header for dependent slices by copying from an independent + /// slice. + pub fn replace_header(&mut self, header: SliceHeader) -> Result<(), String> { + if !self.header.dependent_slice_segment_flag { + Err("Replacing the slice header is only possible for dependent slices".into()) + } else { + let first_slice_segment_in_pic_flag = self.header.first_slice_segment_in_pic_flag; + let no_output_of_prior_pics_flag = self.header.no_output_of_prior_pics_flag; + let pic_parameter_set_id = self.header.pic_parameter_set_id; + let dependent_slice_segment_flag = self.header.dependent_slice_segment_flag; + let segment_address = self.header.segment_address; + + let offset_len_minus1 = self.header.offset_len_minus1; + let entry_point_offset_minus1 = self.header.entry_point_offset_minus1; + let num_pic_total_curr = self.header.num_pic_total_curr; + let header_bit_size = self.header.header_bit_size; + let n_emulation_prevention_bytes = self.header.n_emulation_prevention_bytes; + let curr_rps_idx = self.header.curr_rps_idx; + let st_rps_bits = self.header.st_rps_bits; + + self.header = header; + + self.header.first_slice_segment_in_pic_flag = first_slice_segment_in_pic_flag; + self.header.no_output_of_prior_pics_flag = no_output_of_prior_pics_flag; + self.header.pic_parameter_set_id = pic_parameter_set_id; + self.header.dependent_slice_segment_flag = dependent_slice_segment_flag; + self.header.segment_address = segment_address; + self.header.offset_len_minus1 = offset_len_minus1; + self.header.entry_point_offset_minus1 = entry_point_offset_minus1; + self.header.num_pic_total_curr = num_pic_total_curr; + self.header.header_bit_size = header_bit_size; + self.header.n_emulation_prevention_bytes = n_emulation_prevention_bytes; + self.header.curr_rps_idx = curr_rps_idx; + self.header.st_rps_bits = st_rps_bits; + + Ok(()) + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SublayerHrdParameters { + // NOTE: The value of CpbCnt is cpb_cnt_minus1[i] + 1, and cpb_cnt_minus1 + // ranges from 0..=31 + /// `bit_rate_value_minus1[ i ]` (together with bit_rate_scale) specifies the + /// maximum input bit rate for the i-th CPB when the CPB operates at the + /// access unit level + pub bit_rate_value_minus1: [u32; 32], + /// `cpb_size_value_minus1[ i ]` is used together with cpb_size_scale to + /// specify the i-th CPB size when the CPB operates at the access unit + /// level. + pub cpb_size_value_minus1: [u32; 32], + /// `cpb_size_du_value_minus1[ i ]` is used together with cpb_size_du_scale to + /// specify the i-th CPB size when the CPB operates at sub-picture level. + pub cpb_size_du_value_minus1: [u32; 32], + /// `bit_rate_du_value_minus1[ i ]` (together with bit_rate_scale) specifies + /// the maximum input bit rate for the i-th CPB when the CPB operates at the + /// sub-picture level. + pub bit_rate_du_value_minus1: [u32; 32], + /// `cbr_flag[ i ]` not set specifies that to decode this CVS by the HRD using + /// the i-th CPB specification. + pub cbr_flag: [bool; 32], +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HrdParams { + /// When set, specifies that NAL HRD parameters (pertaining to the Type II + /// bitstream conformance point) are present in the hrd_parameters( ) syntax + /// structure. When not set, specifies that NAL HRD parameters are not + /// present in the hrd_parameters( ) syntax structure. + pub nal_hrd_parameters_present_flag: bool, + /// When set, specifies that VCL HRD parameters (pertaining to the Type I + /// bitstream conformance point) are present in the hrd_parameters( ) syntax + /// structure. When not set, specifies that VCL HRD parameters are not + /// present in the hrd_parameters( ) syntax structure. + pub vcl_hrd_parameters_present_flag: bool, + /// When set, specifies that sub-picture level HRD parameters are present + /// and the HRD may operate at access unit level or sub-picture level. When + /// not set, specifies that sub-picture level HRD parameters are not present + /// and the HRD operates at access unit level. + pub sub_pic_hrd_params_present_flag: bool, + /// Used to specify the clock sub-tick. A clock sub-tick is the minimum + /// interval of time that can be represented in the coded data when + /// sub_pic_hrd_params_present_flag is set. + pub tick_divisor_minus2: u8, + /// du_cpb_removal_delay_increment_length_minus1 plus 1 specifies the + /// length, in bits, of the `du_cpb_removal_delay_increment_minus1[ i ]` and + /// du_common_cpb_removal_delay_increment_minus1 syntax elements of the + /// picture timing SEI message and the du_spt_cpb_removal_delay_increment + /// syntax element in the decoding unit information SEI message. + pub du_cpb_removal_delay_increment_length_minus1: u8, + /// When set, specifies that sub-picture level CPB removal delay parameters + /// are present in picture timing SEI messages and no decoding unit + /// information SEI message is available (in the CVS or provided through + /// external means not specified in this Specification). When not set, + /// specifies that sub-picture level CPB removal delay parameters are + /// present in decoding unit information SEI messages and picture timing SEI + /// messages do not include sub-picture level CPB removal delay parameters. + pub sub_pic_cpb_params_in_pic_timing_sei_flag: bool, + /// dpb_output_delay_du_length_minus1 plus 1 specifies the length, in bits, + /// of the pic_dpb_output_du_delay syntax element in the picture timing SEI + /// message and the pic_spt_dpb_output_du_delay syntax element in the + /// decoding unit information SEI message. + pub dpb_output_delay_du_length_minus1: u8, + /// Together with `bit_rate_value_minus1[ i ]`, specifies the maximum input + /// bit rate of the i-th CPB. + pub bit_rate_scale: u8, + /// Together with `cpb_size_du_value_minus1[ i ]`, specifies the CPB size of + /// the i-th CPB when the CPB operates at sub-picture level. + pub cpb_size_scale: u8, + /// Together with `cpb_size_du_value_minus1[ i ]`, specifies the CPB size of + /// the i-th CPB when the CPB operates at sub-picture level. + pub cpb_size_du_scale: u8, + /// initial_cpb_removal_delay_length_minus1 plus 1 specifies the length, in + /// bits, of the `nal_initial_cpb_removal_delay[ i ]`, + /// `nal_initial_cpb_removal_offset[ i ]`, `vcl_initial_cpb_removal_delay[ i ]` + /// and `vcl_initial_cpb_removal_offset[ i ]` syntax elements of the buffering + /// period SEI message. + pub initial_cpb_removal_delay_length_minus1: u8, + /// au_cpb_removal_delay_length_minus1 plus 1 specifies the length, in bits, + /// of the cpb_delay_offset syntax element in the buffering period SEI + /// message and the au_cpb_removal_delay_minus1 syntax element in the + /// picture timing SEI message. + pub au_cpb_removal_delay_length_minus1: u8, + /// dpb_output_delay_length_minus1 plus 1 specifies the length, in bits, of + /// the dpb_delay_offset syntax element in the buffering period SEI message + /// and the pic_dpb_output_delay syntax element in the picture timing SEI + /// message. + pub dpb_output_delay_length_minus1: u8, + /// `fixed_pic_rate_general_flag[ i ]` set indicates that, when HighestTid is + /// equal to i, the temporal distance between the HRD output times of + /// consecutive pictures in output order is constrained as specified in the + /// specification. `fixed_pic_rate_general_flag[ i ]` not set indicates that + /// this constraint may not apply. + pub fixed_pic_rate_general_flag: [bool; 7], + /// `fixed_pic_rate_within_cvs_flag[ i ]` set indicates that, when HighestTid + /// is equal to i, the temporal distance between the HRD output times of + /// consecutive pictures in output order is constrained as specified in the + /// specification. `fixed_pic_rate_within_cvs_flag[ i ]` not set indicates + /// that this constraint may not apply. + pub fixed_pic_rate_within_cvs_flag: [bool; 7], + /// `elemental_duration_in_tc_minus1[ i ]` plus 1 (when present) specifies, + /// when HighestTid is equal to i, the temporal distance, in clock ticks, + /// between the elemental units that specify the HRD output times of + /// consecutive pictures in output order as specified in the specification. + pub elemental_duration_in_tc_minus1: [u32; 7], + /// `low_delay_hrd_flag[ i ]` specifies the HRD operational mode, when + /// HighestTid is equal to i, as specified in Annex C or clause F.13. + pub low_delay_hrd_flag: [bool; 7], + /// `cpb_cnt_minus1[ i ]` plus 1 specifies the number of alternative CPB + /// specifications in the bitstream of the CVS when HighestTid is equal to + /// i. + pub cpb_cnt_minus1: [u32; 7], + /// The NAL HRD data. + pub nal_hrd: [SublayerHrdParameters; 7], + /// The VCL HRD data. + pub vcl_hrd: [SublayerHrdParameters; 7], +} + +impl Default for HrdParams { + fn default() -> Self { + Self { + initial_cpb_removal_delay_length_minus1: 23, + au_cpb_removal_delay_length_minus1: 23, + dpb_output_delay_du_length_minus1: 23, + nal_hrd_parameters_present_flag: Default::default(), + vcl_hrd_parameters_present_flag: Default::default(), + sub_pic_hrd_params_present_flag: Default::default(), + tick_divisor_minus2: Default::default(), + du_cpb_removal_delay_increment_length_minus1: Default::default(), + sub_pic_cpb_params_in_pic_timing_sei_flag: Default::default(), + bit_rate_scale: Default::default(), + cpb_size_scale: Default::default(), + cpb_size_du_scale: Default::default(), + dpb_output_delay_length_minus1: Default::default(), + fixed_pic_rate_general_flag: Default::default(), + fixed_pic_rate_within_cvs_flag: Default::default(), + elemental_duration_in_tc_minus1: Default::default(), + low_delay_hrd_flag: Default::default(), + cpb_cnt_minus1: Default::default(), + nal_hrd: Default::default(), + vcl_hrd: Default::default(), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VuiParams { + /// When set, specifies that aspect_ratio_idc is present. When not set, + /// specifies that aspect_ratio_idc is not present. + pub aspect_ratio_info_present_flag: bool, + /// Specifies the value of the sample aspect ratio of the luma samples. + pub aspect_ratio_idc: u32, + /// Indicates the horizontal size of the sample aspect ratio (in arbitrary + /// units). + pub sar_width: u32, + /// Indicates the vertical size of the sample aspect ratio (in arbitrary + /// units). + pub sar_height: u32, + /// When set, specifies that the overscan_appropriate_flag is present. When + /// not set, the preferred display method for the video signal is + /// unspecified. + pub overscan_info_present_flag: bool, + /// When set indicates that the cropped decoded pictures output are suitable + /// for display using overscan. When not set, indicates that the cropped + /// decoded pictures output contain visually important information in the + /// entire region out to the edges of the conformance cropping window of the + /// picture, such that the cropped decoded pictures output should not be + /// displayed using overscan. + pub overscan_appropriate_flag: bool, + /// When set, specifies that video_format, video_full_range_flag and + /// colour_description_present_flag are present. When not set, specify that + /// video_format, video_full_range_flag and colour_description_present_flag + /// are not present. + pub video_signal_type_present_flag: bool, + /// Indicates the representation of the pictures as specified in Table E.2, + /// before being coded in accordance with this Specification. + pub video_format: u8, + /// Indicates the black level and range of the luma and chroma signals as + /// derived from E′Y, E′PB, and E′PR or E′R, E′G, and E′B real-valued + /// component signals. + pub video_full_range_flag: bool, + /// When set, specifies that colour_primaries, transfer_characteristics, and + /// matrix_coeffs are present. When not set, specifies that + /// colour_primaries, transfer_characteristics, and matrix_coeffs are not + /// present. + pub colour_description_present_flag: bool, + /// Indicates the chromaticity coordinates of the source primaries as + /// specified in Table E.3 in terms of the CIE 1931 definition of x and y as + /// specified in ISO 11664-1. + pub colour_primaries: u32, + /// See table E.4 in the specification. + pub transfer_characteristics: u32, + /// Describes the matrix coefficients used in deriving luma and chroma + /// signals from the green, blue, and red, or Y, Z, and X primaries, as + /// specified in Table E.5. + pub matrix_coeffs: u32, + /// When true, specifies that chroma_sample_loc_type_top_field and + /// chroma_sample_loc_type_bottom_field are present. When false, specifies + /// that chroma_sample_loc_type_top_field and + /// chroma_sample_loc_type_bottom_field are not present. + pub chroma_loc_info_present_flag: bool, + /// See the specification for more details. + pub chroma_sample_loc_type_top_field: u32, + /// See the specification for more details. + pub chroma_sample_loc_type_bottom_field: u32, + /// When true, indicates that the value of all decoded chroma samples is + /// equal to 1 << ( BitDepthC − 1 ). When false, provides no indication of + /// decoded chroma sample values. + pub neutral_chroma_indication_flag: bool, + /// When true, indicates that the CVS conveys pictures that represent + /// fields, and specifies that a picture timing SEI message shall be present + /// in every access unit of the current CVS. When false, indicates that the + /// CVS conveys pictures that represent frames and that a picture timing SEI + /// message may or may not be present in any access unit of the current CVS. + pub field_seq_flag: bool, + /// When true, specifies that picture timing SEI messages are present for + /// every picture and include the pic_struct, source_scan_type and + /// duplicate_flag syntax elements. When false, specifies that the + /// pic_struct syntax element is not present in picture timing SEI messages. + pub frame_field_info_present_flag: bool, + /// When true, indicates that the default display window parameters follow + /// next in the VUI. When false, indicates that the default display window + /// parameters are not present. + pub default_display_window_flag: bool, + /// Specifies the samples of the pictures in the CVS that are within the + /// default display window, in terms of a rectangular region specified in + /// picture coordinates for display. + pub def_disp_win_left_offset: u32, + /// Specifies the samples of the pictures in the CVS that are within the + /// default display window, in terms of a rectangular region specified in + /// picture coordinates for display. + pub def_disp_win_right_offset: u32, + /// Specifies the samples of the pictures in the CVS that are within the + /// default display window, in terms of a rectangular region specified in + /// picture coordinates for display. + pub def_disp_win_top_offset: u32, + /// Specifies the samples of the pictures in the CVS that are within the + /// default display window, in terms of a rectangular region specified in + /// picture coordinates for display. + pub def_disp_win_bottom_offset: u32, + /// When set, specifies that vui_num_units_in_tick, vui_time_scale, + /// vui_poc_proportional_to_timing_flag and vui_hrd_parameters_present_flag + /// are present in the vui_parameters( ) syntax structure. When not set, + /// specifies that vui_num_units_in_tick, vui_time_scale, + /// vui_poc_proportional_to_timing_flag and vui_hrd_parameters_present_flag + /// are not present in the vui_parameters( ) syntax structure + pub timing_info_present_flag: bool, + /// The number of time units of a clock operating at the frequency + /// vui_time_scale Hz that corresponds to one increment (called a clock + /// tick) of a clock tick counter. + pub num_units_in_tick: u32, + /// Is the number of time units that pass in one second. For example, a time + /// coordinate system that measures time using a 27 MHz clock has a + /// vui_time_scale of 27 000 000. + pub time_scale: u32, + /// When set, indicates that the picture order count value for each picture + /// in the CVS that is not the first picture in the CVS, in decoding order, + /// is proportional to the output time of the picture relative to the output + /// time of the first picture in the CVS. When not set, indicates that the + /// picture order count value for each picture in the CVS that is not the + /// first picture in the CVS, in decoding order, may or may not be + /// proportional to the output time of the picture relative to the output + /// time of the first picture in the CVS. + pub poc_proportional_to_timing_flag: bool, + /// vui_num_ticks_poc_diff_one_minus1 plus 1 specifies the number of clock + /// ticks corresponding to a difference of picture order count values equal + /// to 1. + pub num_ticks_poc_diff_one_minus1: u32, + /// When set, specifies that the syntax structure hrd_parameters( ) is + /// present in the vui_parameters( ) syntax structure. When not set, + /// specifies that the syntax structure hrd_parameters( ) is not present in + /// the vui_parameters( ) syntax structure. + pub hrd_parameters_present_flag: bool, + /// The hrd_parameters() data. + pub hrd: HrdParams, + /// When set, specifies that the bitstream restriction parameters for the + /// CVS are present. When not set, specifies that the bitstream restriction + /// parameters for the CVS are not present. + pub bitstream_restriction_flag: bool, + /// When set, indicates that each PPS that is active in the CVS has the same + /// value of the syntax elements num_tile_columns_minus1, + /// num_tile_rows_minus1, uniform_spacing_flag, `column_width_minus1[ i ]`, + /// `row_height_minus1[ i ]` and loop_filter_across_tiles_enabled_flag, when + /// present. When not set, indicates that tiles syntax elements in different + /// PPSs may or may not have the same value + pub tiles_fixed_structure_flag: bool, + /// When not set, indicates that no sample outside the picture boundaries + /// and no sample at a fractional sample position for which the sample value + /// is derived using one or more samples outside the picture boundaries is + /// used for inter prediction of any sample. When set, indicates that one + /// or more samples outside the picture boundaries may be used in inter + /// prediction. + pub motion_vectors_over_pic_boundaries_flag: bool, + /// When set, indicates that all P and B slices (when present) that belong + /// to the same picture have an identical reference picture list 0 and that + /// all B slices (when present) that belong to the same picture have an + /// identical reference picture list 1. + pub restricted_ref_pic_lists_flag: bool, + /// When not equal to 0, establishes a bound on the maximum possible size of + /// distinct coded spatial segmentation regions in the pictures of the CVS. + pub min_spatial_segmentation_idc: u32, + /// Indicates a number of bytes not exceeded by the sum of the sizes of the + /// VCL NAL units associated with any coded picture in the CVS. + pub max_bytes_per_pic_denom: u32, + /// Indicates an upper bound for the number of coded bits of coding_unit( ) + /// data for anycoding block in any picture of the CVS. + pub max_bits_per_min_cu_denom: u32, + /// Indicate the maximum absolute value of a decoded horizontal and vertical + /// motion vector component, respectively, in quarter luma sample units, for + /// all pictures in the CVS. + pub log2_max_mv_length_horizontal: u32, + /// Indicate the maximum absolute value of a decoded horizontal and vertical + /// motion vector component, respectively, in quarter luma sample units, for + /// all pictures in the CVS. + pub log2_max_mv_length_vertical: u32, +} + +impl Default for VuiParams { + fn default() -> Self { + Self { + aspect_ratio_info_present_flag: Default::default(), + aspect_ratio_idc: Default::default(), + sar_width: Default::default(), + sar_height: Default::default(), + overscan_info_present_flag: Default::default(), + overscan_appropriate_flag: Default::default(), + video_signal_type_present_flag: Default::default(), + video_format: 5, + video_full_range_flag: Default::default(), + colour_description_present_flag: Default::default(), + colour_primaries: 2, + transfer_characteristics: 2, + matrix_coeffs: 2, + chroma_loc_info_present_flag: Default::default(), + chroma_sample_loc_type_top_field: Default::default(), + chroma_sample_loc_type_bottom_field: Default::default(), + neutral_chroma_indication_flag: Default::default(), + field_seq_flag: Default::default(), + frame_field_info_present_flag: Default::default(), + default_display_window_flag: Default::default(), + def_disp_win_left_offset: Default::default(), + def_disp_win_right_offset: Default::default(), + def_disp_win_top_offset: Default::default(), + def_disp_win_bottom_offset: Default::default(), + timing_info_present_flag: Default::default(), + num_units_in_tick: Default::default(), + time_scale: Default::default(), + poc_proportional_to_timing_flag: Default::default(), + num_ticks_poc_diff_one_minus1: Default::default(), + hrd_parameters_present_flag: Default::default(), + hrd: Default::default(), + bitstream_restriction_flag: Default::default(), + tiles_fixed_structure_flag: Default::default(), + motion_vectors_over_pic_boundaries_flag: true, + restricted_ref_pic_lists_flag: Default::default(), + min_spatial_segmentation_idc: Default::default(), + max_bytes_per_pic_denom: 2, + max_bits_per_min_cu_denom: 1, + log2_max_mv_length_horizontal: 15, + log2_max_mv_length_vertical: 15, + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct Parser { + active_vpses: BTreeMap>, + active_spses: BTreeMap>, + active_ppses: BTreeMap>, +} + +impl Parser { + /// Parse a VPS NALU. + pub fn parse_vps(&mut self, nalu: &Nalu) -> Result<&Vps, String> { + if !matches!(nalu.header.type_, NaluType::VpsNut) { + return Err(format!( + "Invalid NALU type, expected {:?}, got {:?}", + NaluType::VpsNut, + nalu.header.type_ + )); + } + + let data = nalu.as_ref(); + let header = &nalu.header; + let hdr_len = header.len(); + // Skip the header + let mut r = BitReader::new(&data[hdr_len..], true); + + let mut vps = Vps { + video_parameter_set_id: r.read_bits(4)?, + base_layer_internal_flag: r.read_bit()?, + base_layer_available_flag: r.read_bit()?, + max_layers_minus1: r.read_bits(6)?, + max_sub_layers_minus1: r.read_bits(3)?, + temporal_id_nesting_flag: r.read_bit()?, + ..Default::default() + }; + + r.skip_bits(16)?; // vps_reserved_0xffff_16bits + + let ptl = &mut vps.profile_tier_level; + Self::parse_profile_tier_level(ptl, &mut r, true, vps.max_sub_layers_minus1)?; + + vps.sub_layer_ordering_info_present_flag = r.read_bit()?; + + let start = + if vps.sub_layer_ordering_info_present_flag { 0 } else { vps.max_sub_layers_minus1 } + as usize; + + for i in start..=usize::from(vps.max_sub_layers_minus1) { + vps.max_dec_pic_buffering_minus1[i] = r.read_ue_max(15)?; + vps.max_num_reorder_pics[i] = r.read_ue_max(vps.max_dec_pic_buffering_minus1[i])?; + vps.max_latency_increase_plus1[i] = r.read_ue()?; + + if i > 0 { + if vps.max_dec_pic_buffering_minus1[i] < vps.max_dec_pic_buffering_minus1[i - 1] { + return Err(format!( + "Invalid max_dec_pic_buffering_minus1[{}]: {}", + i, vps.max_dec_pic_buffering_minus1[i] + )); + } + + if vps.max_num_reorder_pics[i] < vps.max_num_reorder_pics[i - 1] { + return Err(format!( + "Invalid max_num_reorder_pics[{}]: {}", + i, vps.max_num_reorder_pics[i] + )); + } + } + } + + // vps_sub_layer_ordering_info_present_flag equal to 0 specifies that + // the values of vps_max_dec_pic_buffering_minus1[ + // vps_max_sub_layers_minus1 ], vps_max_num_reorder_pics[ vps_max_sub_ + // layers_minus1 ] and vps_max_latency_increase_plus1[ + // vps_max_sub_layers_minus1 ] apply to all sub-layers + if !vps.sub_layer_ordering_info_present_flag { + let max_num_sublayers = usize::from(vps.max_sub_layers_minus1); + for i in 0..max_num_sublayers { + vps.max_dec_pic_buffering_minus1[i] = + vps.max_dec_pic_buffering_minus1[max_num_sublayers]; + + vps.max_num_reorder_pics[i] = vps.max_num_reorder_pics[max_num_sublayers]; + + vps.max_latency_increase_plus1[i] = + vps.max_latency_increase_plus1[max_num_sublayers]; + } + } + + vps.max_layer_id = r.read_bits(6)?; + if vps.max_layer_id > 62 { + return Err(format!("Invalid max_layer_id {}", vps.max_layer_id)); + } + + vps.num_layer_sets_minus1 = r.read_ue_max(1023)?; + + for _ in 1..=vps.num_layer_sets_minus1 { + for _ in 0..=vps.max_layer_id { + // Skip layer_id_included_flag[i][j] for now. + r.skip_bits(1)?; + } + } + + vps.timing_info_present_flag = r.read_bit()?; + + if vps.timing_info_present_flag { + vps.num_units_in_tick = r.read_bits::(31)? << 1; + vps.num_units_in_tick |= r.read_bits::(1)?; + + vps.time_scale = r.read_bits::(31)? << 1; + vps.time_scale |= r.read_bits::(1)?; + + vps.poc_proportional_to_timing_flag = r.read_bit()?; + if vps.poc_proportional_to_timing_flag { + vps.num_ticks_poc_diff_one_minus1 = r.read_ue()?; + } + + vps.num_hrd_parameters = r.read_ue()?; + + for i in 0..vps.num_hrd_parameters as usize { + vps.hrd_layer_set_idx.push(r.read_ue()?); + if i > 0 { + vps.cprms_present_flag.push(r.read_bit()?); + } + + let mut hrd = HrdParams::default(); + Self::parse_hrd_parameters( + vps.cprms_present_flag[i], + vps.max_sub_layers_minus1, + &mut hrd, + &mut r, + )?; + + vps.hrd_parameters.push(hrd); + } + } + + vps.extension_flag = r.read_bit()?; + + if self.active_vpses.keys().len() >= MAX_VPS_COUNT { + return Err("Broken data: Number of active VPSs > MAX_VPS_COUNT".into()); + } + + let key = vps.video_parameter_set_id; + let vps = Rc::new(vps); + self.active_vpses.remove(&key); + Ok(self.active_vpses.entry(key).or_insert(vps)) + } + + fn parse_profile_tier_level( + ptl: &mut ProfileTierLevel, + r: &mut BitReader, + profile_present_flag: bool, + sps_max_sub_layers_minus_1: u8, + ) -> Result<(), String> { + if profile_present_flag { + ptl.general_profile_space = r.read_bits(2)?; + ptl.general_tier_flag = r.read_bit()?; + ptl.general_profile_idc = r.read_bits(5)?; + + for i in 0..32 { + ptl.general_profile_compatibility_flag[i] = r.read_bit()?; + } + + ptl.general_progressive_source_flag = r.read_bit()?; + ptl.general_interlaced_source_flag = r.read_bit()?; + ptl.general_non_packed_constraint_flag = r.read_bit()?; + ptl.general_frame_only_constraint_flag = r.read_bit()?; + + if ptl.general_profile_idc == 4 + || ptl.general_profile_compatibility_flag[4] + || ptl.general_profile_idc == 5 + || ptl.general_profile_compatibility_flag[5] + || ptl.general_profile_idc == 6 + || ptl.general_profile_compatibility_flag[6] + || ptl.general_profile_idc == 7 + || ptl.general_profile_compatibility_flag[7] + || ptl.general_profile_idc == 8 + || ptl.general_profile_compatibility_flag[8] + || ptl.general_profile_idc == 9 + || ptl.general_profile_compatibility_flag[9] + || ptl.general_profile_idc == 10 + || ptl.general_profile_compatibility_flag[10] + || ptl.general_profile_idc == 11 + || ptl.general_profile_compatibility_flag[11] + { + ptl.general_max_12bit_constraint_flag = r.read_bit()?; + ptl.general_max_10bit_constraint_flag = r.read_bit()?; + ptl.general_max_8bit_constraint_flag = r.read_bit()?; + ptl.general_max_422chroma_constraint_flag = r.read_bit()?; + ptl.general_max_420chroma_constraint_flag = r.read_bit()?; + ptl.general_max_monochrome_constraint_flag = r.read_bit()?; + ptl.general_intra_constraint_flag = r.read_bit()?; + ptl.general_one_picture_only_constraint_flag = r.read_bit()?; + ptl.general_lower_bit_rate_constraint_flag = r.read_bit()?; + if ptl.general_profile_idc == 5 + || ptl.general_profile_compatibility_flag[5] + || ptl.general_profile_idc == 9 + || ptl.general_profile_compatibility_flag[9] + || ptl.general_profile_idc == 10 + || ptl.general_profile_compatibility_flag[10] + || ptl.general_profile_idc == 11 + || ptl.general_profile_compatibility_flag[11] + { + ptl.general_max_14bit_constraint_flag = r.read_bit()?; + // Skip general_reserved_zero_33bits + r.skip_bits(31)?; + r.skip_bits(2)?; + } else { + // Skip general_reserved_zero_34bits + r.skip_bits(31)?; + r.skip_bits(3)?; + } + } else if ptl.general_profile_idc == 2 || ptl.general_profile_compatibility_flag[2] { + // Skip general_reserved_zero_7bits + r.skip_bits(7)?; + ptl.general_one_picture_only_constraint_flag = r.read_bit()?; + // Skip general_reserved_zero_35bits + r.skip_bits(31)?; + r.skip_bits(4)?; + } else { + r.skip_bits(31)?; + r.skip_bits(12)?; + } + + if ptl.general_profile_idc == 1 + || ptl.general_profile_compatibility_flag[1] + || ptl.general_profile_idc == 2 + || ptl.general_profile_compatibility_flag[2] + || ptl.general_profile_idc == 3 + || ptl.general_profile_compatibility_flag[3] + || ptl.general_profile_idc == 4 + || ptl.general_profile_compatibility_flag[4] + || ptl.general_profile_idc == 5 + || ptl.general_profile_compatibility_flag[5] + || ptl.general_profile_idc == 9 + || ptl.general_profile_compatibility_flag[9] + || ptl.general_profile_idc == 11 + || ptl.general_profile_compatibility_flag[11] + { + ptl.general_inbld_flag = r.read_bit()?; + } else { + r.skip_bits(1)?; + } + } + + let level: u8 = r.read_bits(8)?; + ptl.general_level_idc = Level::try_from(level)?; + + for i in 0..sps_max_sub_layers_minus_1 as usize { + ptl.sub_layer_profile_present_flag[i] = r.read_bit()?; + ptl.sub_layer_level_present_flag[i] = r.read_bit()?; + } + + if sps_max_sub_layers_minus_1 > 0 { + for _ in sps_max_sub_layers_minus_1..8 { + r.skip_bits(2)?; + } + } + + for i in 0..sps_max_sub_layers_minus_1 as usize { + if ptl.sub_layer_level_present_flag[i] { + ptl.sub_layer_profile_space[i] = r.read_bits(2)?; + ptl.sub_layer_tier_flag[i] = r.read_bit()?; + ptl.sub_layer_profile_idc[i] = r.read_bits(5)?; + for j in 0..32 { + ptl.sub_layer_profile_compatibility_flag[i][j] = r.read_bit()?; + } + ptl.sub_layer_progressive_source_flag[i] = r.read_bit()?; + ptl.sub_layer_interlaced_source_flag[i] = r.read_bit()?; + ptl.sub_layer_non_packed_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_frame_only_constraint_flag[i] = r.read_bit()?; + + if ptl.sub_layer_profile_idc[i] == 4 + || ptl.sub_layer_profile_compatibility_flag[i][4] + || ptl.sub_layer_profile_idc[i] == 5 + || ptl.sub_layer_profile_compatibility_flag[i][5] + || ptl.sub_layer_profile_idc[i] == 6 + || ptl.sub_layer_profile_compatibility_flag[i][6] + || ptl.sub_layer_profile_idc[i] == 7 + || ptl.sub_layer_profile_compatibility_flag[i][7] + || ptl.sub_layer_profile_idc[i] == 8 + || ptl.sub_layer_profile_compatibility_flag[i][8] + || ptl.sub_layer_profile_idc[i] == 9 + || ptl.sub_layer_profile_compatibility_flag[i][9] + || ptl.sub_layer_profile_idc[i] == 10 + || ptl.sub_layer_profile_compatibility_flag[i][10] + || ptl.sub_layer_profile_idc[i] == 11 + || ptl.sub_layer_profile_compatibility_flag[i][11] + { + ptl.sub_layer_max_12bit_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_max_10bit_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_max_8bit_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_max_422chroma_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_max_420chroma_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_max_monochrome_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_intra_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_one_picture_only_constraint_flag[i] = r.read_bit()?; + ptl.sub_layer_lower_bit_rate_constraint_flag[i] = r.read_bit()?; + + if ptl.sub_layer_profile_idc[i] == 5 + || ptl.sub_layer_profile_compatibility_flag[i][5] + || ptl.sub_layer_profile_idc[i] == 9 + || ptl.sub_layer_profile_compatibility_flag[i][9] + || ptl.sub_layer_profile_idc[i] == 10 + || ptl.sub_layer_profile_compatibility_flag[i][10] + || ptl.sub_layer_profile_idc[i] == 11 + || ptl.sub_layer_profile_compatibility_flag[i][11] + { + ptl.sub_layer_max_14bit_constraint_flag[i] = r.read_bit()?; + r.skip_bits(33)?; + } else { + r.skip_bits(34)?; + } + } else if ptl.sub_layer_profile_idc[i] == 2 + || ptl.sub_layer_profile_compatibility_flag[i][2] + { + r.skip_bits(7)?; + ptl.sub_layer_one_picture_only_constraint_flag[i] = r.read_bit()?; + r.skip_bits(35)?; + } else { + r.skip_bits(43)?; + } + + if ptl.sub_layer_profile_idc[i] == 1 + || ptl.sub_layer_profile_compatibility_flag[i][1] + || ptl.sub_layer_profile_idc[i] == 2 + || ptl.sub_layer_profile_compatibility_flag[i][2] + || ptl.sub_layer_profile_idc[i] == 3 + || ptl.sub_layer_profile_compatibility_flag[i][3] + || ptl.sub_layer_profile_idc[i] == 4 + || ptl.sub_layer_profile_compatibility_flag[i][4] + || ptl.sub_layer_profile_idc[i] == 5 + || ptl.sub_layer_profile_compatibility_flag[i][5] + || ptl.sub_layer_profile_idc[i] == 9 + || ptl.sub_layer_profile_compatibility_flag[i][9] + || ptl.sub_layer_profile_idc[i] == 11 + || ptl.sub_layer_profile_compatibility_flag[i][11] + { + ptl.sub_layer_inbld_flag[i] = r.read_bit()?; + } else { + r.skip_bits(1)?; + } + + if ptl.sub_layer_level_present_flag[i] { + let level: u8 = r.read_bits(8)?; + ptl.sub_layer_level_idc[i] = Level::try_from(level)?; + } + } + } + Ok(()) + } + + fn fill_default_scaling_list(sl: &mut ScalingLists, size_id: i32, matrix_id: i32) { + if size_id == 0 { + sl.scaling_list_4x4[matrix_id as usize] = DEFAULT_SCALING_LIST_0; + return; + } + + let dst = match size_id { + 1 => &mut sl.scaling_list_8x8[matrix_id as usize], + 2 => &mut sl.scaling_list_16x16[matrix_id as usize], + 3 => &mut sl.scaling_list_32x32[matrix_id as usize], + _ => panic!("Invalid size_id {}", size_id), + }; + + let src = if matrix_id < 3 { + &DEFAULT_SCALING_LIST_1 + } else if matrix_id <= 5 { + &DEFAULT_SCALING_LIST_2 + } else { + panic!("Invalid matrix_id {}", matrix_id); + }; + + *dst = *src; + + // When `scaling_list_pred_mode_flag[ sizeId ]`[ matrixId ] is equal to + // 0, scaling_list_pred_matrix_id_ `delta[ sizeId ]`[ matrixId ] is equal + // to 0 and sizeId is greater than 1, the value of + // scaling_list_dc_coef_minus8[ sizeId − 2 `][ matrixId ]` is inferred to + // be equal to 8. + // + // Since we are using a slightly different layout here, with two + // different field names (i.e. 16x16, and 32x32), we must differentiate + // between size_id == 2 or size_id == 3. + if size_id == 2 { + sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize] = 8; + } else if size_id == 3 { + sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize] = 8; + } + } + + fn parse_scaling_list_data(sl: &mut ScalingLists, r: &mut BitReader) -> Result<(), String> { + // 7.4.5 + for size_id in 0..4 { + let mut matrix_id = 0; + while matrix_id < 6 { + let scaling_list_pred_mode_flag = r.read_bit()?; + // If `scaling_list_pred_matrix_id_delta[ sizeId ]`[ matrixId ] is + // equal to 0, the scaling list is inferred from the default + // scaling list `ScalingList[ sizeId ]`[ matrixId `][ i ]` as specified + // in Table 7-5 and Table 7-6 for i = 0..Min( 63, ( 1 << ( 4 + ( + // sizeId << 1 ) ) ) − 1 ). + if !scaling_list_pred_mode_flag { + let scaling_list_pred_matrix_id_delta: u32 = r.read_ue()?; + if scaling_list_pred_matrix_id_delta == 0 { + Self::fill_default_scaling_list(sl, size_id, matrix_id); + } else { + // Equation 7-42 + let factor = if size_id == 3 { 3 } else { 1 }; + let ref_matrix_id = + matrix_id as u32 - scaling_list_pred_matrix_id_delta * factor; + if size_id == 0 { + sl.scaling_list_4x4[matrix_id as usize] = + sl.scaling_list_4x4[ref_matrix_id as usize]; + } else { + let src = match size_id { + 1 => sl.scaling_list_8x8[ref_matrix_id as usize], + 2 => sl.scaling_list_16x16[ref_matrix_id as usize], + 3 => sl.scaling_list_32x32[ref_matrix_id as usize], + _ => return Err(format!("Invalid size_id {}", size_id)), + }; + + let dst = match size_id { + 1 => &mut sl.scaling_list_8x8[matrix_id as usize], + 2 => &mut sl.scaling_list_16x16[matrix_id as usize], + 3 => &mut sl.scaling_list_32x32[matrix_id as usize], + _ => return Err(format!("Invalid size_id {}", size_id)), + }; + + *dst = src; + + if size_id == 2 { + sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize] = + sl.scaling_list_dc_coef_minus8_16x16[ref_matrix_id as usize]; + } else if size_id == 3 { + sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize] = + sl.scaling_list_dc_coef_minus8_32x32[ref_matrix_id as usize]; + } + } + } + } else { + let mut next_coef = 8i32; + let coef_num = std::cmp::min(64, 1 << (4 + (size_id << 1))); + + if size_id > 1 { + if size_id == 2 { + sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize] = + r.read_se_bounded(-7, 247)?; + next_coef = + i32::from(sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize]) + + 8; + } else if size_id == 3 { + sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize] = + r.read_se_bounded(-7, 247)?; + next_coef = + i32::from(sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize]) + + 8; + } + } + + for i in 0..coef_num as usize { + let scaling_list_delta_coef: i32 = r.read_se_bounded(-128, 127)?; + next_coef = (next_coef + scaling_list_delta_coef + 256) % 256; + match size_id { + 0 => sl.scaling_list_4x4[matrix_id as usize][i] = next_coef as _, + 1 => sl.scaling_list_8x8[matrix_id as usize][i] = next_coef as _, + 2 => sl.scaling_list_16x16[matrix_id as usize][i] = next_coef as _, + 3 => sl.scaling_list_32x32[matrix_id as usize][i] = next_coef as _, + _ => return Err(format!("Invalid size_id {}", size_id)), + } + } + } + let step = if size_id == 3 { 3 } else { 1 }; + matrix_id += step; + } + } + Ok(()) + } + + fn parse_short_term_ref_pic_set( + sps: &Sps, + st: &mut ShortTermRefPicSet, + r: &mut BitReader, + st_rps_idx: u8, + ) -> Result<(), String> { + if st_rps_idx != 0 { + st.inter_ref_pic_set_prediction_flag = r.read_bit()?; + } + + // (7-59) + if st.inter_ref_pic_set_prediction_flag { + if st_rps_idx == sps.num_short_term_ref_pic_sets { + st.delta_idx_minus1 = r.read_ue_max(st_rps_idx as u32 - 1)?; + } + + st.delta_rps_sign = r.read_bit()?; + // The value of abs_delta_rps_minus1 shall be in the range of 0 to + // 2^15 − 1, inclusive. + st.abs_delta_rps_minus1 = r.read_ue_max(32767)?; + + let ref_rps_idx = st_rps_idx - (st.delta_idx_minus1 + 1); + let delta_rps = + (1 - 2 * st.delta_rps_sign as i32) * (st.abs_delta_rps_minus1 as i32 + 1); + + let ref_st = sps + .short_term_ref_pic_set + .get(usize::from(ref_rps_idx)) + .ok_or::("Invalid ref_rps_idx".into())?; + + let mut used_by_curr_pic_flag = [false; 64]; + + // 7.4.8 - defaults to 1 if not present + let mut use_delta_flag = [true; 64]; + + for j in 0..=ref_st.num_delta_pocs as usize { + used_by_curr_pic_flag[j] = r.read_bit()?; + if !used_by_curr_pic_flag[j] { + use_delta_flag[j] = r.read_bit()?; + } + } + + // (7-61) + let mut i = 0; + // Ranges are [a,b[, but the real loop is [b, a], i.e. + // [num_positive_pics - 1, 0]. Use ..= so that b is included when + // rev() is called. + for j in (0..=isize::from(ref_st.num_positive_pics) - 1) + .rev() + .take_while(|j| *j >= 0) + .map(|j| j as usize) + { + let d_poc = ref_st.delta_poc_s1[j] + delta_rps; + if d_poc < 0 && use_delta_flag[usize::from(ref_st.num_negative_pics) + j] { + st.delta_poc_s0[i] = d_poc; + st.used_by_curr_pic_s0[i] = + used_by_curr_pic_flag[usize::from(ref_st.num_negative_pics) + j]; + + i += 1; + } + } + + if delta_rps < 0 && use_delta_flag[ref_st.num_delta_pocs as usize] { + st.delta_poc_s0[i] = delta_rps; + st.used_by_curr_pic_s0[i] = used_by_curr_pic_flag[ref_st.num_delta_pocs as usize]; + + i += 1; + } + + // Let's *not* change the original algorithm in any way. + #[allow(clippy::needless_range_loop)] + for j in 0..ref_st.num_negative_pics as usize { + let d_poc = ref_st.delta_poc_s0[j] + delta_rps; + if d_poc < 0 && use_delta_flag[j] { + st.delta_poc_s0[i] = d_poc; + st.used_by_curr_pic_s0[i] = used_by_curr_pic_flag[j]; + + i += 1; + } + } + + st.num_negative_pics = i as u8; + + // (7-62) + let mut i = 0; + // Ranges are [a,b[, but the real loop is [b, a], i.e. + // [num_negative_pics - 1, 0]. Use ..= so that b is included when + // rev() is called. + for j in (0..=isize::from(ref_st.num_negative_pics) - 1) + .rev() + .take_while(|j| *j >= 0) + .map(|j| j as usize) + { + let d_poc = ref_st.delta_poc_s0[j] + delta_rps; + if d_poc > 0 && use_delta_flag[j] { + st.delta_poc_s1[i] = d_poc; + st.used_by_curr_pic_s1[i] = used_by_curr_pic_flag[j]; + + i += 1; + } + } + + if delta_rps > 0 && use_delta_flag[ref_st.num_delta_pocs as usize] { + st.delta_poc_s1[i] = delta_rps; + st.used_by_curr_pic_s1[i] = used_by_curr_pic_flag[ref_st.num_delta_pocs as usize]; + + i += 1; + } + + for j in 0..usize::from(ref_st.num_positive_pics) { + let d_poc = ref_st.delta_poc_s1[j] + delta_rps; + if d_poc > 0 && use_delta_flag[ref_st.num_negative_pics as usize + j] { + st.delta_poc_s1[i] = d_poc; + st.used_by_curr_pic_s1[i] = + used_by_curr_pic_flag[ref_st.num_negative_pics as usize + j]; + + i += 1; + } + } + + st.num_positive_pics = i as u8; + } else { + st.num_negative_pics = r.read_ue_max(u32::from( + sps.max_dec_pic_buffering_minus1[usize::from(sps.max_sub_layers_minus1)], + ))?; + + st.num_positive_pics = r.read_ue_max(u32::from( + sps.max_dec_pic_buffering_minus1[usize::from(sps.max_sub_layers_minus1)] + - st.num_negative_pics, + ))?; + + for i in 0..usize::from(st.num_negative_pics) { + let delta_poc_s0_minus1: u32 = r.read_ue_max(32767)?; + + if i == 0 { + st.delta_poc_s0[i] = -(delta_poc_s0_minus1 as i32 + 1); + } else { + st.delta_poc_s0[i] = st.delta_poc_s0[i - 1] - (delta_poc_s0_minus1 as i32 + 1); + } + + st.used_by_curr_pic_s0[i] = r.read_bit()?; + } + + for i in 0..usize::from(st.num_positive_pics) { + let delta_poc_s1_minus1: u32 = r.read_ue_max(32767)?; + + if i == 0 { + st.delta_poc_s1[i] = delta_poc_s1_minus1 as i32 + 1; + } else { + st.delta_poc_s1[i] = st.delta_poc_s1[i - 1] + (delta_poc_s1_minus1 as i32 + 1); + } + + st.used_by_curr_pic_s1[i] = r.read_bit()?; + } + } + + st.num_delta_pocs = u32::from(st.num_negative_pics + st.num_positive_pics); + + Ok(()) + } + + fn parse_sublayer_hrd_parameters( + h: &mut SublayerHrdParameters, + cpb_cnt: u32, + sub_pic_hrd_params_present_flag: bool, + r: &mut BitReader, + ) -> Result<(), String> { + for i in 0..cpb_cnt as usize { + h.bit_rate_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?; + h.cpb_size_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?; + if sub_pic_hrd_params_present_flag { + h.cpb_size_du_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?; + h.bit_rate_du_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?; + } + + h.cbr_flag[i] = r.read_bit()?; + } + + Ok(()) + } + + fn parse_hrd_parameters( + common_inf_present_flag: bool, + max_num_sublayers_minus1: u8, + hrd: &mut HrdParams, + r: &mut BitReader, + ) -> Result<(), String> { + if common_inf_present_flag { + hrd.nal_hrd_parameters_present_flag = r.read_bit()?; + hrd.vcl_hrd_parameters_present_flag = r.read_bit()?; + if hrd.nal_hrd_parameters_present_flag || hrd.vcl_hrd_parameters_present_flag { + hrd.sub_pic_hrd_params_present_flag = r.read_bit()?; + if hrd.sub_pic_hrd_params_present_flag { + hrd.tick_divisor_minus2 = r.read_bits(8)?; + hrd.du_cpb_removal_delay_increment_length_minus1 = r.read_bits(5)?; + hrd.sub_pic_cpb_params_in_pic_timing_sei_flag = r.read_bit()?; + hrd.dpb_output_delay_du_length_minus1 = r.read_bits(5)?; + } + hrd.bit_rate_scale = r.read_bits(4)?; + hrd.cpb_size_scale = r.read_bits(4)?; + if hrd.sub_pic_hrd_params_present_flag { + hrd.cpb_size_du_scale = r.read_bits(4)?; + } + hrd.initial_cpb_removal_delay_length_minus1 = r.read_bits(5)?; + hrd.au_cpb_removal_delay_length_minus1 = r.read_bits(5)?; + hrd.dpb_output_delay_length_minus1 = r.read_bits(5)?; + } + } + + for i in 0..=max_num_sublayers_minus1 as usize { + hrd.fixed_pic_rate_general_flag[i] = r.read_bit()?; + if !hrd.fixed_pic_rate_general_flag[i] { + hrd.fixed_pic_rate_within_cvs_flag[i] = r.read_bit()?; + } + if hrd.fixed_pic_rate_within_cvs_flag[i] { + hrd.elemental_duration_in_tc_minus1[i] = r.read_ue_max(2047)?; + } else { + hrd.low_delay_hrd_flag[i] = r.read_bit()?; + } + + if !hrd.low_delay_hrd_flag[i] { + hrd.cpb_cnt_minus1[i] = r.read_ue_max(31)?; + } + + if hrd.nal_hrd_parameters_present_flag { + Self::parse_sublayer_hrd_parameters( + &mut hrd.nal_hrd[i], + hrd.cpb_cnt_minus1[i] + 1, + hrd.sub_pic_hrd_params_present_flag, + r, + )?; + } + + if hrd.vcl_hrd_parameters_present_flag { + Self::parse_sublayer_hrd_parameters( + &mut hrd.vcl_hrd[i], + hrd.cpb_cnt_minus1[i] + 1, + hrd.sub_pic_hrd_params_present_flag, + r, + )?; + } + } + + Ok(()) + } + + fn parse_vui_parameters(sps: &mut Sps, r: &mut BitReader) -> Result<(), String> { + let vui = &mut sps.vui_parameters; + + vui.aspect_ratio_info_present_flag = r.read_bit()?; + if vui.aspect_ratio_info_present_flag { + vui.aspect_ratio_idc = r.read_bits(8)?; + const EXTENDED_SAR: u32 = 255; + if vui.aspect_ratio_idc == EXTENDED_SAR { + vui.sar_width = r.read_bits(16)?; + vui.sar_height = r.read_bits(16)?; + } + } + + vui.overscan_info_present_flag = r.read_bit()?; + if vui.overscan_info_present_flag { + vui.overscan_appropriate_flag = r.read_bit()?; + } + + vui.video_signal_type_present_flag = r.read_bit()?; + if vui.video_signal_type_present_flag { + vui.video_format = r.read_bits(3)?; + vui.video_full_range_flag = r.read_bit()?; + vui.colour_description_present_flag = r.read_bit()?; + if vui.colour_description_present_flag { + vui.colour_primaries = r.read_bits(8)?; + vui.transfer_characteristics = r.read_bits(8)?; + vui.matrix_coeffs = r.read_bits(8)?; + } + } + + vui.chroma_loc_info_present_flag = r.read_bit()?; + if vui.chroma_loc_info_present_flag { + vui.chroma_sample_loc_type_top_field = r.read_ue_max(5)?; + vui.chroma_sample_loc_type_bottom_field = r.read_ue_max(5)?; + } + + vui.neutral_chroma_indication_flag = r.read_bit()?; + vui.field_seq_flag = r.read_bit()?; + vui.frame_field_info_present_flag = r.read_bit()?; + vui.default_display_window_flag = r.read_bit()?; + + if vui.default_display_window_flag { + vui.def_disp_win_left_offset = r.read_ue()?; + vui.def_disp_win_right_offset = r.read_ue()?; + vui.def_disp_win_top_offset = r.read_ue()?; + vui.def_disp_win_bottom_offset = r.read_ue()?; + } + + vui.timing_info_present_flag = r.read_bit()?; + if vui.timing_info_present_flag { + vui.num_units_in_tick = r.read_bits::(31)? << 1; + vui.num_units_in_tick |= r.read_bits::(1)?; + + if vui.num_units_in_tick == 0 { + log::warn!("Incompliant value for num_units_in_tick {}", vui.num_units_in_tick); + } + + vui.time_scale = r.read_bits::(31)? << 1; + vui.time_scale |= r.read_bits::(1)?; + + if vui.time_scale == 0 { + log::warn!("Incompliant value for time_scale {}", vui.time_scale); + } + + vui.poc_proportional_to_timing_flag = r.read_bit()?; + if vui.poc_proportional_to_timing_flag { + vui.num_ticks_poc_diff_one_minus1 = r.read_ue_max((2u64.pow(32) - 2) as u32)?; + } + + vui.hrd_parameters_present_flag = r.read_bit()?; + if vui.hrd_parameters_present_flag { + let sps_max_sub_layers_minus1 = sps.max_sub_layers_minus1; + Self::parse_hrd_parameters(true, sps_max_sub_layers_minus1, &mut vui.hrd, r)?; + } + } + + vui.bitstream_restriction_flag = r.read_bit()?; + if vui.bitstream_restriction_flag { + vui.tiles_fixed_structure_flag = r.read_bit()?; + vui.motion_vectors_over_pic_boundaries_flag = r.read_bit()?; + vui.restricted_ref_pic_lists_flag = r.read_bit()?; + + vui.min_spatial_segmentation_idc = r.read_ue_max(4095)?; + vui.max_bytes_per_pic_denom = r.read_ue()?; + vui.max_bits_per_min_cu_denom = r.read_ue()?; + vui.log2_max_mv_length_horizontal = r.read_ue_max(16)?; + vui.log2_max_mv_length_vertical = r.read_ue_max(15)?; + } + + Ok(()) + } + + fn parse_sps_scc_extension(sps: &mut Sps, r: &mut BitReader) -> Result<(), String> { + let scc = &mut sps.scc_extension; + + scc.curr_pic_ref_enabled_flag = r.read_bit()?; + scc.palette_mode_enabled_flag = r.read_bit()?; + if scc.palette_mode_enabled_flag { + scc.palette_max_size = r.read_ue_max(64)?; + scc.delta_palette_max_predictor_size = + r.read_ue_max(128 - u32::from(scc.palette_max_size))?; + scc.palette_predictor_initializers_present_flag = r.read_bit()?; + if scc.palette_predictor_initializers_present_flag { + let max = + u32::from(scc.palette_max_size + scc.delta_palette_max_predictor_size - 1); + scc.num_palette_predictor_initializer_minus1 = r.read_ue_max(max)?; + + let num_comps = if sps.chroma_format_idc == 0 { 1 } else { 3 }; + for comp in 0..num_comps { + for i in 0..=usize::from(scc.num_palette_predictor_initializer_minus1) { + let num_bits = if comp == 0 { + sps.bit_depth_luma_minus8 + 8 + } else { + sps.bit_depth_chroma_minus8 + 8 + }; + scc.palette_predictor_initializer[comp][i] = + r.read_bits(usize::from(num_bits))?; + } + } + } + } + + scc.motion_vector_resolution_control_idc = r.read_bits(2)?; + scc.intra_boundary_filtering_disabled_flag = r.read_bit()?; + + Ok(()) + } + + fn parse_sps_range_extension(sps: &mut Sps, r: &mut BitReader) -> Result<(), String> { + let ext = &mut sps.range_extension; + + ext.transform_skip_rotation_enabled_flag = r.read_bit()?; + ext.transform_skip_context_enabled_flag = r.read_bit()?; + ext.implicit_rdpcm_enabled_flag = r.read_bit()?; + ext.explicit_rdpcm_enabled_flag = r.read_bit()?; + ext.extended_precision_processing_flag = r.read_bit()?; + ext.intra_smoothing_disabled_flag = r.read_bit()?; + ext.high_precision_offsets_enabled_flag = r.read_bit()?; + ext.persistent_rice_adaptation_enabled_flag = r.read_bit()?; + ext.cabac_bypass_alignment_enabled_flag = r.read_bit()?; + + Ok(()) + } + + /// Parse a SPS NALU. + pub fn parse_sps(&mut self, nalu: &Nalu) -> Result<&Sps, String> { + if !matches!(nalu.header.type_, NaluType::SpsNut) { + return Err(format!( + "Invalid NALU type, expected {:?}, got {:?}", + NaluType::SpsNut, + nalu.header.type_ + )); + } + + let data = nalu.as_ref(); + let header = &nalu.header; + let hdr_len = header.len(); + // Skip the header + let mut r = BitReader::new(&data[hdr_len..], true); + + let video_parameter_set_id = r.read_bits(4)?; + + // A non-existing VPS means the SPS is not using any VPS. + let vps = self.get_vps(video_parameter_set_id).cloned(); + + let mut sps = Sps { + video_parameter_set_id, + max_sub_layers_minus1: r.read_bits(3)?, + temporal_id_nesting_flag: r.read_bit()?, + vps, + ..Default::default() + }; + + Self::parse_profile_tier_level( + &mut sps.profile_tier_level, + &mut r, + true, + sps.max_sub_layers_minus1, + )?; + + sps.seq_parameter_set_id = r.read_ue_max(MAX_SPS_COUNT as u32 - 1)?; + sps.chroma_format_idc = r.read_ue_max(3)?; + + if sps.chroma_format_idc == 3 { + sps.separate_colour_plane_flag = r.read_bit()?; + } + + sps.chroma_array_type = + if sps.separate_colour_plane_flag { 0 } else { sps.chroma_format_idc }; + + sps.pic_width_in_luma_samples = r.read_ue_bounded(1, 16888)?; + sps.pic_height_in_luma_samples = r.read_ue_bounded(1, 16888)?; + + sps.conformance_window_flag = r.read_bit()?; + if sps.conformance_window_flag { + sps.conf_win_left_offset = r.read_ue()?; + sps.conf_win_right_offset = r.read_ue()?; + sps.conf_win_top_offset = r.read_ue()?; + sps.conf_win_bottom_offset = r.read_ue()?; + } + + sps.bit_depth_luma_minus8 = r.read_ue_max(6)?; + sps.bit_depth_chroma_minus8 = r.read_ue_max(6)?; + sps.log2_max_pic_order_cnt_lsb_minus4 = r.read_ue_max(12)?; + sps.sub_layer_ordering_info_present_flag = r.read_bit()?; + + { + let i = if sps.sub_layer_ordering_info_present_flag { + 0 + } else { + sps.max_sub_layers_minus1 + }; + + for j in i..=sps.max_sub_layers_minus1 { + sps.max_dec_pic_buffering_minus1[j as usize] = r.read_ue_max(16)?; + sps.max_num_reorder_pics[j as usize] = + r.read_ue_max(sps.max_dec_pic_buffering_minus1[j as usize] as _)?; + sps.max_latency_increase_plus1[j as usize] = r.read_ue_max(u32::MAX - 1)?; + } + } + + sps.log2_min_luma_coding_block_size_minus3 = r.read_ue_max(3)?; + sps.log2_diff_max_min_luma_coding_block_size = r.read_ue_max(6)?; + sps.log2_min_luma_transform_block_size_minus2 = r.read_ue_max(3)?; + sps.log2_diff_max_min_luma_transform_block_size = r.read_ue_max(3)?; + + // (7-10) + sps.min_cb_log2_size_y = u32::from(sps.log2_min_luma_coding_block_size_minus3 + 3); + // (7-11) + sps.ctb_log2_size_y = + sps.min_cb_log2_size_y + u32::from(sps.log2_diff_max_min_luma_coding_block_size); + // (7-12) + sps.ctb_size_y = 1 << sps.ctb_log2_size_y; + // (7-17) + sps.pic_height_in_ctbs_y = + (sps.pic_height_in_luma_samples as f64 / sps.ctb_size_y as f64).ceil() as u32; + // (7-15) + sps.pic_width_in_ctbs_y = + (sps.pic_width_in_luma_samples as f64 / sps.ctb_size_y as f64).ceil() as u32; + + sps.max_tb_log2_size_y = u32::from( + sps.log2_min_luma_transform_block_size_minus2 + + 2 + + sps.log2_diff_max_min_luma_transform_block_size, + ); + + sps.pic_size_in_samples_y = + u32::from(sps.pic_width_in_luma_samples) * u32::from(sps.pic_height_in_luma_samples); + + if sps.max_tb_log2_size_y > std::cmp::min(sps.ctb_log2_size_y, 5) { + return Err(format!("Invalid value for MaxTbLog2SizeY: {}", sps.max_tb_log2_size_y)); + } + + sps.pic_size_in_ctbs_y = sps.pic_width_in_ctbs_y * sps.pic_height_in_ctbs_y; + + sps.max_transform_hierarchy_depth_inter = r.read_ue_max(4)?; + sps.max_transform_hierarchy_depth_intra = r.read_ue_max(4)?; + + sps.scaling_list_enabled_flag = r.read_bit()?; + if sps.scaling_list_enabled_flag { + sps.scaling_list_data_present_flag = r.read_bit()?; + if sps.scaling_list_data_present_flag { + Self::parse_scaling_list_data(&mut sps.scaling_list, &mut r)?; + } + } + + sps.amp_enabled_flag = r.read_bit()?; + sps.sample_adaptive_offset_enabled_flag = r.read_bit()?; + + sps.pcm_enabled_flag = r.read_bit()?; + if sps.pcm_enabled_flag { + sps.pcm_sample_bit_depth_luma_minus1 = r.read_bits(4)?; + sps.pcm_sample_bit_depth_chroma_minus1 = r.read_bits(4)?; + sps.log2_min_pcm_luma_coding_block_size_minus3 = r.read_ue_max(2)?; + sps.log2_diff_max_min_pcm_luma_coding_block_size = r.read_ue_max(2)?; + sps.pcm_loop_filter_disabled_flag = r.read_bit()?; + } + + sps.num_short_term_ref_pic_sets = r.read_ue_max(64)?; + + for i in 0..sps.num_short_term_ref_pic_sets { + let mut st = ShortTermRefPicSet::default(); + Self::parse_short_term_ref_pic_set(&sps, &mut st, &mut r, i)?; + sps.short_term_ref_pic_set.push(st); + } + + sps.long_term_ref_pics_present_flag = r.read_bit()?; + if sps.long_term_ref_pics_present_flag { + sps.num_long_term_ref_pics_sps = r.read_ue_max(32)?; + for i in 0..usize::from(sps.num_long_term_ref_pics_sps) { + sps.lt_ref_pic_poc_lsb_sps[i] = + r.read_bits(usize::from(sps.log2_max_pic_order_cnt_lsb_minus4) + 4)?; + sps.used_by_curr_pic_lt_sps_flag[i] = r.read_bit()?; + } + } + + sps.temporal_mvp_enabled_flag = r.read_bit()?; + sps.strong_intra_smoothing_enabled_flag = r.read_bit()?; + + sps.vui_parameters_present_flag = r.read_bit()?; + if sps.vui_parameters_present_flag { + Self::parse_vui_parameters(&mut sps, &mut r)?; + } + + sps.extension_present_flag = r.read_bit()?; + if sps.extension_present_flag { + sps.range_extension_flag = r.read_bit()?; + if sps.range_extension_flag { + Self::parse_sps_range_extension(&mut sps, &mut r)?; + } + + let multilayer_extension_flag = r.read_bit()?; + if multilayer_extension_flag { + return Err("Multilayer extension not supported.".into()); + } + + let three_d_extension_flag = r.read_bit()?; + if three_d_extension_flag { + return Err("3D extension not supported.".into()); + } + + sps.scc_extension_flag = r.read_bit()?; + if sps.scc_extension_flag { + Self::parse_sps_scc_extension(&mut sps, &mut r)?; + } + } + + let shift = if sps.range_extension.high_precision_offsets_enabled_flag { + sps.bit_depth_luma_minus8 + 7 + } else { + 7 + }; + + sps.wp_offset_half_range_y = 1 << shift; + + let shift = if sps.range_extension.high_precision_offsets_enabled_flag { + sps.bit_depth_chroma_minus8 + 7 + } else { + 7 + }; + + sps.wp_offset_half_range_c = 1 << shift; + + log::debug!( + "Parsed SPS({}), resolution: ({}, {}): NAL size was {}", + sps.seq_parameter_set_id, + sps.width(), + sps.height(), + nalu.size + ); + + if self.active_spses.keys().len() >= MAX_SPS_COUNT { + return Err("Broken data: Number of active SPSs > MAX_SPS_COUNT".into()); + } + + let key = sps.seq_parameter_set_id; + let sps = Rc::new(sps); + self.active_spses.remove(&key); + Ok(self.active_spses.entry(key).or_insert(sps)) + } + + fn parse_pps_scc_extension(pps: &mut Pps, sps: &Sps, r: &mut BitReader) -> Result<(), String> { + let scc = &mut pps.scc_extension; + scc.curr_pic_ref_enabled_flag = r.read_bit()?; + scc.residual_adaptive_colour_transform_enabled_flag = r.read_bit()?; + if scc.residual_adaptive_colour_transform_enabled_flag { + scc.slice_act_qp_offsets_present_flag = r.read_bit()?; + scc.act_y_qp_offset_plus5 = r.read_se_bounded(-7, 17)?; + scc.act_cb_qp_offset_plus5 = r.read_se_bounded(-7, 17)?; + scc.act_cr_qp_offset_plus3 = r.read_se_bounded(-9, 15)?; + } + + scc.palette_predictor_initializers_present_flag = r.read_bit()?; + if scc.palette_predictor_initializers_present_flag { + let max = sps.scc_extension.palette_max_size + + sps.scc_extension.delta_palette_max_predictor_size; + scc.num_palette_predictor_initializers = r.read_ue_max(max.into())?; + if scc.num_palette_predictor_initializers > 0 { + scc.monochrome_palette_flag = r.read_bit()?; + scc.luma_bit_depth_entry_minus8 = r.read_ue_bounded( + sps.bit_depth_luma_minus8.into(), + sps.bit_depth_luma_minus8.into(), + )?; + if !scc.monochrome_palette_flag { + scc.chroma_bit_depth_entry_minus8 = r.read_ue_bounded( + sps.bit_depth_chroma_minus8.into(), + sps.bit_depth_chroma_minus8.into(), + )?; + } + + let num_comps = if scc.monochrome_palette_flag { 1 } else { 3 }; + for comp in 0..num_comps { + let num_bits = if comp == 0 { + scc.luma_bit_depth_entry_minus8 + 8 + } else { + scc.chroma_bit_depth_entry_minus8 + 8 + }; + for i in 0..usize::from(scc.num_palette_predictor_initializers) { + scc.palette_predictor_initializer[comp][i] = + r.read_bits(num_bits.into())?; + } + } + } + } + Ok(()) + } + + fn parse_pps_range_extension( + pps: &mut Pps, + sps: &Sps, + r: &mut BitReader, + ) -> Result<(), String> { + let rext = &mut pps.range_extension; + + if pps.transform_skip_enabled_flag { + rext.log2_max_transform_skip_block_size_minus2 = + r.read_ue_max(sps.max_tb_log2_size_y - 2)?; + } + + rext.cross_component_prediction_enabled_flag = r.read_bit()?; + rext.chroma_qp_offset_list_enabled_flag = r.read_bit()?; + if rext.chroma_qp_offset_list_enabled_flag { + rext.diff_cu_chroma_qp_offset_depth = r.read_ue()?; + rext.chroma_qp_offset_list_len_minus1 = r.read_ue_max(5)?; + for i in 0..=rext.chroma_qp_offset_list_len_minus1 as usize { + rext.cb_qp_offset_list[i] = r.read_se_bounded(-12, 12)?; + rext.cr_qp_offset_list[i] = r.read_se_bounded(-12, 12)?; + } + } + + let bit_depth_y = sps.bit_depth_luma_minus8 + 8; + let max = u32::from(std::cmp::max(0, bit_depth_y - 10)); + + rext.log2_sao_offset_scale_luma = r.read_ue_max(max)?; + rext.log2_sao_offset_scale_chroma = r.read_ue_max(max)?; + + Ok(()) + } + + /// Parse a PPS NALU. + pub fn parse_pps(&mut self, nalu: &Nalu) -> Result<&Pps, String> { + if !matches!(nalu.header.type_, NaluType::PpsNut) { + return Err(format!( + "Invalid NALU type, expected {:?}, got {:?}", + NaluType::PpsNut, + nalu.header.type_ + )); + } + + let data = nalu.as_ref(); + let header = &nalu.header; + let hdr_len = header.len(); + // Skip the header + let mut r = BitReader::new(&data[hdr_len..], true); + + let pic_parameter_set_id = r.read_ue_max(MAX_PPS_COUNT as u32 - 1)?; + let seq_parameter_set_id = r.read_ue_max(MAX_SPS_COUNT as u32 - 1)?; + + let sps = self.get_sps(seq_parameter_set_id).ok_or::(format!( + "Could not get SPS for seq_parameter_set_id {}", + seq_parameter_set_id + ))?; + + let mut pps = Pps { + pic_parameter_set_id, + seq_parameter_set_id, + dependent_slice_segments_enabled_flag: Default::default(), + output_flag_present_flag: Default::default(), + num_extra_slice_header_bits: Default::default(), + sign_data_hiding_enabled_flag: Default::default(), + cabac_init_present_flag: Default::default(), + num_ref_idx_l0_default_active_minus1: Default::default(), + num_ref_idx_l1_default_active_minus1: Default::default(), + init_qp_minus26: Default::default(), + constrained_intra_pred_flag: Default::default(), + transform_skip_enabled_flag: Default::default(), + cu_qp_delta_enabled_flag: Default::default(), + diff_cu_qp_delta_depth: Default::default(), + cb_qp_offset: Default::default(), + cr_qp_offset: Default::default(), + slice_chroma_qp_offsets_present_flag: Default::default(), + weighted_pred_flag: Default::default(), + weighted_bipred_flag: Default::default(), + transquant_bypass_enabled_flag: Default::default(), + tiles_enabled_flag: Default::default(), + entropy_coding_sync_enabled_flag: Default::default(), + num_tile_columns_minus1: Default::default(), + num_tile_rows_minus1: Default::default(), + uniform_spacing_flag: true, + column_width_minus1: Default::default(), + row_height_minus1: Default::default(), + loop_filter_across_tiles_enabled_flag: true, + loop_filter_across_slices_enabled_flag: Default::default(), + deblocking_filter_control_present_flag: Default::default(), + deblocking_filter_override_enabled_flag: Default::default(), + deblocking_filter_disabled_flag: Default::default(), + beta_offset_div2: Default::default(), + tc_offset_div2: Default::default(), + scaling_list_data_present_flag: Default::default(), + scaling_list: Default::default(), + lists_modification_present_flag: Default::default(), + log2_parallel_merge_level_minus2: Default::default(), + slice_segment_header_extension_present_flag: Default::default(), + extension_present_flag: Default::default(), + range_extension_flag: Default::default(), + range_extension: Default::default(), + qp_bd_offset_y: Default::default(), + scc_extension: Default::default(), + scc_extension_flag: Default::default(), + sps: Rc::clone(sps), + }; + + pps.dependent_slice_segments_enabled_flag = r.read_bit()?; + pps.output_flag_present_flag = r.read_bit()?; + pps.num_extra_slice_header_bits = r.read_bits(3)?; + pps.sign_data_hiding_enabled_flag = r.read_bit()?; + pps.cabac_init_present_flag = r.read_bit()?; + + // 7.4.7.1 + pps.num_ref_idx_l0_default_active_minus1 = r.read_ue_max(14)?; + pps.num_ref_idx_l1_default_active_minus1 = r.read_ue_max(14)?; + + // (7-5) + let qp_bd_offset_y = 6 * i32::from(sps.bit_depth_luma_minus8); + + pps.init_qp_minus26 = r.read_se_bounded(-(26 + qp_bd_offset_y), 25)?; + pps.qp_bd_offset_y = qp_bd_offset_y as u32; + pps.constrained_intra_pred_flag = r.read_bit()?; + pps.transform_skip_enabled_flag = r.read_bit()?; + pps.cu_qp_delta_enabled_flag = r.read_bit()?; + + if pps.cu_qp_delta_enabled_flag { + pps.diff_cu_qp_delta_depth = + r.read_ue_max(u32::from(sps.log2_diff_max_min_luma_coding_block_size))?; + } + + pps.cb_qp_offset = r.read_se_bounded(-12, 12)?; + pps.cr_qp_offset = r.read_se_bounded(-12, 12)?; + + pps.slice_chroma_qp_offsets_present_flag = r.read_bit()?; + pps.weighted_pred_flag = r.read_bit()?; + pps.weighted_bipred_flag = r.read_bit()?; + pps.transquant_bypass_enabled_flag = r.read_bit()?; + pps.tiles_enabled_flag = r.read_bit()?; + pps.entropy_coding_sync_enabled_flag = r.read_bit()?; + + // A mix of the rbsp data and the algorithm in 6.5.1 + if pps.tiles_enabled_flag { + pps.num_tile_columns_minus1 = r.read_ue_max(sps.pic_width_in_ctbs_y - 1)?; + pps.num_tile_rows_minus1 = r.read_ue_max(sps.pic_height_in_ctbs_y - 1)?; + pps.uniform_spacing_flag = r.read_bit()?; + if !pps.uniform_spacing_flag { + pps.column_width_minus1[usize::from(pps.num_tile_columns_minus1)] = + sps.pic_width_in_ctbs_y - 1; + + for i in 0..usize::from(pps.num_tile_columns_minus1) { + pps.column_width_minus1[i] = r.read_ue_max( + pps.column_width_minus1[usize::from(pps.num_tile_columns_minus1)] - 1, + )?; + pps.column_width_minus1[usize::from(pps.num_tile_columns_minus1)] -= + pps.column_width_minus1[i] + 1; + } + + pps.row_height_minus1[usize::from(pps.num_tile_rows_minus1)] = + sps.pic_height_in_ctbs_y - 1; + + for i in 0..usize::from(pps.num_tile_rows_minus1) { + pps.row_height_minus1[i] = r.read_ue_max( + pps.row_height_minus1[usize::from(pps.num_tile_rows_minus1)] - 1, + )?; + pps.row_height_minus1[usize::from(pps.num_tile_rows_minus1)] -= + pps.row_height_minus1[i] + 1; + } + } else { + let nrows = u32::from(pps.num_tile_rows_minus1) + 1; + let ncols = u32::from(pps.num_tile_columns_minus1) + 1; + + for j in 0..ncols { + pps.column_width_minus1[j as usize] = ((j + 1) * sps.pic_width_in_ctbs_y) + / ncols + - j * sps.pic_width_in_ctbs_y / ncols + - 1; + } + + for j in 0..nrows { + pps.row_height_minus1[j as usize] = ((j + 1) * sps.pic_height_in_ctbs_y) + / nrows + - j * sps.pic_height_in_ctbs_y / nrows + - 1; + } + } + + pps.loop_filter_across_tiles_enabled_flag = r.read_bit()?; + } + + pps.loop_filter_across_slices_enabled_flag = r.read_bit()?; + pps.deblocking_filter_control_present_flag = r.read_bit()?; + + if pps.deblocking_filter_control_present_flag { + pps.deblocking_filter_override_enabled_flag = r.read_bit()?; + pps.deblocking_filter_disabled_flag = r.read_bit()?; + if !pps.deblocking_filter_disabled_flag { + pps.beta_offset_div2 = r.read_se_bounded(-6, 6)?; + pps.tc_offset_div2 = r.read_se_bounded(-6, 6)?; + } + } + + pps.scaling_list_data_present_flag = r.read_bit()?; + + if pps.scaling_list_data_present_flag { + Self::parse_scaling_list_data(&mut pps.scaling_list, &mut r)?; + } else { + for size_id in 0..4 { + let mut matrix_id = 0; + while matrix_id < 6 { + Self::fill_default_scaling_list(&mut pps.scaling_list, size_id, matrix_id); + let step = if size_id == 3 { 3 } else { 1 }; + matrix_id += step; + } + } + } + + pps.lists_modification_present_flag = r.read_bit()?; + pps.log2_parallel_merge_level_minus2 = r.read_ue_max(sps.ctb_log2_size_y - 2)?; + pps.slice_segment_header_extension_present_flag = r.read_bit()?; + + pps.extension_present_flag = r.read_bit()?; + if pps.extension_present_flag { + pps.range_extension_flag = r.read_bit()?; + + if pps.range_extension_flag { + Self::parse_pps_range_extension(&mut pps, sps, &mut r)?; + } + + let multilayer_extension_flag = r.read_bit()?; + if multilayer_extension_flag { + return Err("Multilayer extension is not supported".into()); + } + + let three_d_extension_flag = r.read_bit()?; + if three_d_extension_flag { + return Err("3D extension is not supported".into()); + } + + pps.scc_extension_flag = r.read_bit()?; + if pps.scc_extension_flag { + Self::parse_pps_scc_extension(&mut pps, sps, &mut r)?; + } + + r.skip_bits(4)?; // pps_extension_4bits + } + + log::debug!("Parsed PPS({}), NAL size was {}", pps.pic_parameter_set_id, nalu.size); + + if self.active_ppses.keys().len() >= MAX_PPS_COUNT { + return Err("Broken Data: number of active PPSs > MAX_PPS_COUNT".into()); + } + + let key = pps.pic_parameter_set_id; + let pps = Rc::new(pps); + self.active_ppses.remove(&key); + Ok(self.active_ppses.entry(key).or_insert(pps)) + } + + fn parse_pred_weight_table( + hdr: &mut SliceHeader, + r: &mut BitReader, + sps: &Sps, + ) -> Result<(), String> { + let pwt = &mut hdr.pred_weight_table; + + pwt.luma_log2_weight_denom = r.read_ue_max(7)?; + if sps.chroma_array_type != 0 { + pwt.delta_chroma_log2_weight_denom = r.read_se()?; + pwt.chroma_log2_weight_denom = (pwt.luma_log2_weight_denom as i32 + + pwt.delta_chroma_log2_weight_denom as i32) + .try_into() + .map_err(|_| { + String::from("Integer overflow on chroma_log2_weight_denom calculation") + })?; + } + + for i in 0..=usize::from(hdr.num_ref_idx_l0_active_minus1) { + pwt.luma_weight_l0_flag[i] = r.read_bit()?; + } + + if sps.chroma_array_type != 0 { + for i in 0..=usize::from(hdr.num_ref_idx_l0_active_minus1) { + pwt.chroma_weight_l0_flag[i] = r.read_bit()?; + } + } + + for i in 0..=usize::from(hdr.num_ref_idx_l0_active_minus1) { + if pwt.luma_weight_l0_flag[i] { + pwt.delta_luma_weight_l0[i] = r.read_se_bounded(-128, 127)?; + pwt.luma_offset_l0[i] = r.read_se_bounded(-128, 127)?; + } + + if pwt.chroma_weight_l0_flag[i] { + for j in 0..2 { + pwt.delta_chroma_weight_l0[i][j] = r.read_se_bounded(-128, 127)?; + pwt.delta_chroma_offset_l0[i][j] = r.read_se_bounded( + -4 * sps.wp_offset_half_range_c as i32, + 4 * sps.wp_offset_half_range_c as i32 - 1, + )?; + } + } + } + + if hdr.type_.is_b() { + for i in 0..=usize::from(hdr.num_ref_idx_l1_active_minus1) { + pwt.luma_weight_l1_flag[i] = r.read_bit()?; + } + + if sps.chroma_format_idc != 0 { + for i in 0..=usize::from(hdr.num_ref_idx_l1_active_minus1) { + pwt.chroma_weight_l1_flag[i] = r.read_bit()?; + } + } + + for i in 0..=usize::from(hdr.num_ref_idx_l1_active_minus1) { + if pwt.luma_weight_l1_flag[i] { + pwt.delta_luma_weight_l1[i] = r.read_se_bounded(-128, 127)?; + pwt.luma_offset_l1[i] = r.read_se_bounded(-128, 127)?; + } + + if pwt.chroma_weight_l1_flag[i] { + for j in 0..2 { + pwt.delta_chroma_weight_l1[i][j] = r.read_se_bounded(-128, 127)?; + pwt.delta_chroma_offset_l1[i][j] = r.read_se_bounded( + -4 * sps.wp_offset_half_range_c as i32, + 4 * sps.wp_offset_half_range_c as i32 - 1, + )?; + } + } + } + } + + Ok(()) + } + + fn parse_ref_pic_lists_modification( + hdr: &mut SliceHeader, + r: &mut BitReader, + ) -> Result<(), String> { + let rplm = &mut hdr.ref_pic_list_modification; + + rplm.ref_pic_list_modification_flag_l0 = r.read_bit()?; + if rplm.ref_pic_list_modification_flag_l0 { + for _ in 0..=hdr.num_ref_idx_l0_active_minus1 { + let num_bits = (hdr.num_pic_total_curr as f64).log2().ceil() as _; + + let entry = r.read_bits(num_bits)?; + + if entry > hdr.num_pic_total_curr - 1 { + return Err(format!( + "Invalid list_entry_l0 {}, expected at max NumPicTotalCurr - 1: {}", + entry, + hdr.num_pic_total_curr - 1 + )); + } + + rplm.list_entry_l0.push(entry); + } + } + + if hdr.type_.is_b() { + rplm.ref_pic_list_modification_flag_l1 = r.read_bit()?; + if rplm.ref_pic_list_modification_flag_l1 { + for _ in 0..=hdr.num_ref_idx_l1_active_minus1 { + let num_bits = (hdr.num_pic_total_curr as f64).log2().ceil() as _; + + let entry = r.read_bits(num_bits)?; + + if entry > hdr.num_pic_total_curr - 1 { + return Err(format!( + "Invalid list_entry_l1 {}, expected at max NumPicTotalCurr - 1: {}", + entry, + hdr.num_pic_total_curr - 1 + )); + } + + rplm.list_entry_l1.push(entry); + } + } + } + + Ok(()) + } + + /// Further sets default values given `sps` and `pps`. + pub fn slice_header_set_defaults(hdr: &mut SliceHeader, sps: &Sps, pps: &Pps) { + // Set some defaults that can't be defined in Default::default(). + hdr.deblocking_filter_disabled_flag = pps.deblocking_filter_disabled_flag; + hdr.beta_offset_div2 = pps.beta_offset_div2; + hdr.tc_offset_div2 = pps.tc_offset_div2; + hdr.loop_filter_across_slices_enabled_flag = pps.loop_filter_across_slices_enabled_flag; + hdr.curr_rps_idx = sps.num_short_term_ref_pic_sets; + hdr.use_integer_mv_flag = sps.scc_extension.motion_vector_resolution_control_idc != 0; + } + + /// Parses a slice header from a slice NALU. + pub fn parse_slice_header<'a>(&mut self, nalu: Nalu<'a>) -> Result, String> { + if !matches!( + nalu.header.type_, + NaluType::TrailN + | NaluType::TrailR + | NaluType::TsaN + | NaluType::TsaR + | NaluType::StsaN + | NaluType::StsaR + | NaluType::RadlN + | NaluType::RadlR + | NaluType::RaslN + | NaluType::RaslR + | NaluType::BlaWLp + | NaluType::BlaWRadl + | NaluType::BlaNLp + | NaluType::IdrWRadl + | NaluType::IdrNLp + | NaluType::CraNut, + ) { + return Err(format!("Invalid NALU type: {:?} is not a slice NALU", nalu.header.type_)); + } + + let data = nalu.as_ref(); + let nalu_header = &nalu.header; + let hdr_len = nalu_header.len(); + // Skip the header + let mut r = BitReader::new(&data[hdr_len..], true); + + let mut hdr = + SliceHeader { first_slice_segment_in_pic_flag: r.read_bit()?, ..Default::default() }; + + if nalu.header.type_.is_irap() { + hdr.no_output_of_prior_pics_flag = r.read_bit()?; + } + + hdr.pic_parameter_set_id = r.read_ue_max(63)?; + + let pps = self.get_pps(hdr.pic_parameter_set_id).ok_or::(format!( + "Could not get PPS for pic_parameter_set_id {}", + hdr.pic_parameter_set_id + ))?; + + let sps = &pps.sps; + + Self::slice_header_set_defaults(&mut hdr, sps, pps); + + if !hdr.first_slice_segment_in_pic_flag { + if pps.dependent_slice_segments_enabled_flag { + hdr.dependent_slice_segment_flag = r.read_bit()?; + } + + let num_bits = (sps.pic_size_in_ctbs_y as f64).log2().ceil() as _; + hdr.segment_address = r.read_bits(num_bits)?; + + if hdr.segment_address > sps.pic_size_in_ctbs_y - 1 { + return Err(format!("Invalid slice_segment_address {}", hdr.segment_address)); + } + } + + if !hdr.dependent_slice_segment_flag { + r.skip_bits(usize::from(pps.num_extra_slice_header_bits))?; + + let slice_type: u32 = r.read_ue()?; + hdr.type_ = SliceType::try_from(slice_type)?; + + if pps.output_flag_present_flag { + hdr.pic_output_flag = r.read_bit()?; + } + + if sps.separate_colour_plane_flag { + hdr.colour_plane_id = r.read_bits(2)?; + } + + if !matches!(nalu_header.type_, NaluType::IdrWRadl | NaluType::IdrNLp) { + let num_bits = usize::from(sps.log2_max_pic_order_cnt_lsb_minus4 + 4); + hdr.pic_order_cnt_lsb = r.read_bits(num_bits)?; + + if u32::from(hdr.pic_order_cnt_lsb) + > 2u32.pow(u32::from(sps.log2_max_pic_order_cnt_lsb_minus4 + 4)) + { + return Err(format!("Invalid pic_order_cnt_lsb {}", hdr.pic_order_cnt_lsb)); + } + + hdr.short_term_ref_pic_set_sps_flag = r.read_bit()?; + + if !hdr.short_term_ref_pic_set_sps_flag { + let epb_before = r.num_epb(); + let bits_left_before = r.num_bits_left(); + + let st_rps_idx = sps.num_short_term_ref_pic_sets; + + Self::parse_short_term_ref_pic_set( + sps, + &mut hdr.short_term_ref_pic_set, + &mut r, + st_rps_idx, + )?; + + hdr.st_rps_bits = ((bits_left_before - r.num_bits_left()) + - 8 * (r.num_epb() - epb_before)) + as u32; + } else if sps.num_short_term_ref_pic_sets > 1 { + let num_bits = (sps.num_short_term_ref_pic_sets as f64).log2().ceil() as _; + hdr.short_term_ref_pic_set_idx = r.read_bits(num_bits)?; + + if hdr.short_term_ref_pic_set_idx > sps.num_short_term_ref_pic_sets - 1 { + return Err(format!( + "Invalid short_term_ref_pic_set_idx {}", + hdr.short_term_ref_pic_set_idx + )); + } + } + + if hdr.short_term_ref_pic_set_sps_flag { + hdr.curr_rps_idx = hdr.short_term_ref_pic_set_idx; + } + + if sps.long_term_ref_pics_present_flag { + if sps.num_long_term_ref_pics_sps > 0 { + hdr.num_long_term_sps = + r.read_ue_max(u32::from(sps.num_long_term_ref_pics_sps))?; + } + + hdr.num_long_term_pics = r.read_ue_max( + MAX_LONG_TERM_REF_PIC_SETS as u32 - u32::from(hdr.num_long_term_sps), + )?; + + let num_lt = hdr.num_long_term_sps + hdr.num_long_term_pics; + for i in 0..usize::from(num_lt) { + // The variables `PocLsbLt[ i ]` and `UsedByCurrPicLt[ i ]` are derived as follows: + // + // – If i is less than num_long_term_sps, `PocLsbLt[ i ]` is set equal to + // lt_ref_pic_poc_lsb_sps[ `lt_idx_sps[ i ]` ] and `UsedByCurrPicLt[ i ]` is set equal + // to used_by_curr_pic_lt_sps_flag[ `lt_idx_sps[ i ]` ]. + // + // – Otherwise, `PocLsbLt[ i ]` + // is set equal to `poc_lsb_lt[ i ]` and `UsedByCurrPicLt[ i ]` is set equal to + // `used_by_curr_pic_lt_flag[ i ]`. + if i < usize::from(hdr.num_long_term_sps) { + if sps.num_long_term_ref_pics_sps > 1 { + let num_bits = + (sps.num_long_term_ref_pics_sps as f64).log2().ceil() as _; + + hdr.lt_idx_sps[i] = r.read_bits(num_bits)?; + + if hdr.lt_idx_sps[i] > sps.num_long_term_ref_pics_sps - 1 { + return Err(format!( + "Invalid lt_idx_sps[{}] {}", + i, hdr.lt_idx_sps[i] + )); + } + } + + hdr.poc_lsb_lt[i] = + sps.lt_ref_pic_poc_lsb_sps[usize::from(hdr.lt_idx_sps[i])]; + hdr.used_by_curr_pic_lt[i] = + sps.used_by_curr_pic_lt_sps_flag[usize::from(hdr.lt_idx_sps[i])]; + } else { + let num_bits = usize::from(sps.log2_max_pic_order_cnt_lsb_minus4) + 4; + hdr.poc_lsb_lt[i] = r.read_bits(num_bits)?; + hdr.used_by_curr_pic_lt[i] = r.read_bit()?; + } + + hdr.delta_poc_msb_present_flag[i] = r.read_bit()?; + if hdr.delta_poc_msb_present_flag[i] { + // The value of `delta_poc_msb_cycle_lt[ i ]` shall be + // in the range of 0 to 2(32 − + // log2_max_pic_order_cnt_lsb_minus4 − 4 ), + // inclusive. When `delta_poc_msb_cycle_lt[ i ]` is + // not present, it is inferred to be equal to 0. + let max = + 2u32.pow(32 - u32::from(sps.log2_max_pic_order_cnt_lsb_minus4) - 4); + hdr.delta_poc_msb_cycle_lt[i] = r.read_ue_max(max)?; + } + // Equation 7-52 (simplified) + if i != 0 && i != usize::from(hdr.num_long_term_sps) { + hdr.delta_poc_msb_cycle_lt[i] += hdr.delta_poc_msb_cycle_lt[i - 1]; + } + } + } + + if sps.temporal_mvp_enabled_flag { + hdr.temporal_mvp_enabled_flag = r.read_bit()?; + } + } + + if sps.sample_adaptive_offset_enabled_flag { + hdr.sao_luma_flag = r.read_bit()?; + if sps.chroma_array_type != 0 { + hdr.sao_chroma_flag = r.read_bit()?; + } + } + + if hdr.type_.is_p() || hdr.type_.is_b() { + hdr.num_ref_idx_active_override_flag = r.read_bit()?; + if hdr.num_ref_idx_active_override_flag { + hdr.num_ref_idx_l0_active_minus1 = r.read_ue_max(MAX_REF_IDX_ACTIVE - 1)?; + if hdr.type_.is_b() { + hdr.num_ref_idx_l1_active_minus1 = r.read_ue_max(MAX_REF_IDX_ACTIVE - 1)?; + } + } else { + hdr.num_ref_idx_l0_active_minus1 = pps.num_ref_idx_l0_default_active_minus1; + hdr.num_ref_idx_l1_active_minus1 = pps.num_ref_idx_l1_default_active_minus1; + } + + // 7-57 + let mut num_pic_total_curr = 0; + let rps = if hdr.short_term_ref_pic_set_sps_flag { + sps.short_term_ref_pic_set + .get(usize::from(hdr.curr_rps_idx)) + .ok_or::("Invalid RPS".into())? + } else { + &hdr.short_term_ref_pic_set + }; + + for i in 0..usize::from(rps.num_negative_pics) { + if rps.used_by_curr_pic_s0[i] { + num_pic_total_curr += 1; + } + } + + for i in 0..usize::from(rps.num_positive_pics) { + if rps.used_by_curr_pic_s1[i] { + num_pic_total_curr += 1; + } + } + + for i in 0..usize::from(hdr.num_long_term_sps + hdr.num_long_term_pics) { + if hdr.used_by_curr_pic_lt[i] { + num_pic_total_curr += 1; + } + } + + if pps.scc_extension.curr_pic_ref_enabled_flag { + num_pic_total_curr += 1; + } + + hdr.num_pic_total_curr = num_pic_total_curr; + + if pps.lists_modification_present_flag && hdr.num_pic_total_curr > 1 { + Self::parse_ref_pic_lists_modification(&mut hdr, &mut r)?; + } + + if hdr.type_.is_b() { + hdr.mvd_l1_zero_flag = r.read_bit()?; + } + + if pps.cabac_init_present_flag { + hdr.cabac_init_flag = r.read_bit()?; + } + + if hdr.temporal_mvp_enabled_flag { + if hdr.type_.is_b() { + hdr.collocated_from_l0_flag = r.read_bit()?; + } + + if (hdr.collocated_from_l0_flag && hdr.num_ref_idx_l0_active_minus1 > 0) + || (!hdr.collocated_from_l0_flag && hdr.num_ref_idx_l1_active_minus1 > 0) + { + let max = if (hdr.type_.is_p() || hdr.type_.is_b()) + && hdr.collocated_from_l0_flag + { + hdr.num_ref_idx_l0_active_minus1 + } else if hdr.type_.is_b() && !hdr.collocated_from_l0_flag { + hdr.num_ref_idx_l1_active_minus1 + } else { + return Err("Invalid value for collocated_ref_idx".into()); + }; + + { + hdr.collocated_ref_idx = r.read_ue_max(u32::from(max))?; + } + } + } + + if (pps.weighted_pred_flag && hdr.type_.is_p()) + || (pps.weighted_bipred_flag && hdr.type_.is_b()) + { + Self::parse_pred_weight_table(&mut hdr, &mut r, sps)?; + } + + hdr.five_minus_max_num_merge_cand = r.read_ue()?; + + if sps.scc_extension.motion_vector_resolution_control_idc == 2 { + hdr.use_integer_mv_flag = r.read_bit()?; + } + } + + hdr.qp_delta = r.read_se_bounded(-87, 77)?; + + let slice_qp_y = (26 + pps.init_qp_minus26 + hdr.qp_delta) as i32; + if slice_qp_y < -(pps.qp_bd_offset_y as i32) || slice_qp_y > 51 { + return Err(format!("Invalid slice_qp_delta: {}", hdr.qp_delta)); + } + + if pps.slice_chroma_qp_offsets_present_flag { + hdr.cb_qp_offset = r.read_se_bounded(-12, 12)?; + + let qp_offset = pps.cb_qp_offset + hdr.cb_qp_offset; + if !(-12..=12).contains(&qp_offset) { + return Err(format!( + "Invalid value for slice_cb_qp_offset: {}", + hdr.cb_qp_offset + )); + } + + hdr.cr_qp_offset = r.read_se_bounded(-12, 12)?; + + let qp_offset = pps.cr_qp_offset + hdr.cr_qp_offset; + if !(-12..=12).contains(&qp_offset) { + return Err(format!( + "Invalid value for slice_cr_qp_offset: {}", + hdr.cr_qp_offset + )); + } + } + + if pps.scc_extension.slice_act_qp_offsets_present_flag { + hdr.slice_act_y_qp_offset = r.read_se_bounded(-12, 12)?; + hdr.slice_act_cb_qp_offset = r.read_se_bounded(-12, 12)?; + hdr.slice_act_cr_qp_offset = r.read_se_bounded(-12, 12)?; + } + + if pps.range_extension.chroma_qp_offset_list_enabled_flag { + hdr.cu_chroma_qp_offset_enabled_flag = r.read_bit()?; + } + + if pps.deblocking_filter_override_enabled_flag { + hdr.deblocking_filter_override_flag = r.read_bit()?; + } + + if hdr.deblocking_filter_override_flag { + hdr.deblocking_filter_disabled_flag = r.read_bit()?; + if !hdr.deblocking_filter_disabled_flag { + hdr.beta_offset_div2 = r.read_se_bounded(-6, 6)?; + hdr.tc_offset_div2 = r.read_se_bounded(-6, 6)?; + } + } + + if pps.loop_filter_across_slices_enabled_flag + && (hdr.sao_luma_flag + || hdr.sao_chroma_flag + || !hdr.deblocking_filter_disabled_flag) + { + hdr.loop_filter_across_slices_enabled_flag = r.read_bit()?; + } + } + + if pps.tiles_enabled_flag || pps.entropy_coding_sync_enabled_flag { + let max = if !pps.tiles_enabled_flag && pps.entropy_coding_sync_enabled_flag { + sps.pic_height_in_ctbs_y - 1 + } else if pps.tiles_enabled_flag && !pps.entropy_coding_sync_enabled_flag { + u32::from((pps.num_tile_columns_minus1 + 1) * (pps.num_tile_rows_minus1 + 1) - 1) + } else { + (u32::from(pps.num_tile_columns_minus1) + 1) * sps.pic_height_in_ctbs_y - 1 + }; + + hdr.num_entry_point_offsets = r.read_ue_max(max)?; + if hdr.num_entry_point_offsets > 0 { + hdr.offset_len_minus1 = r.read_ue_max(31)?; + for i in 0..hdr.num_entry_point_offsets as usize { + let num_bits = usize::from(hdr.offset_len_minus1 + 1); + hdr.entry_point_offset_minus1[i] = r.read_bits(num_bits)?; + } + } + } + + if pps.slice_segment_header_extension_present_flag { + let segment_header_extension_length = r.read_ue_max(256)?; + for _ in 0..segment_header_extension_length { + r.skip_bits(8)?; // slice_segment_header_extension_data_byte[i] + } + } + + // byte_alignment() + r.skip_bits(1)?; // Alignment bit + let num_bits = r.num_bits_left() % 8; + r.skip_bits(num_bits)?; + + let epb = r.num_epb(); + hdr.header_bit_size = ((nalu.size - epb) * 8 - r.num_bits_left()) as u32; + + hdr.n_emulation_prevention_bytes = epb as u32; + + log::debug!("Parsed slice {:?}, NAL size was {}", nalu_header.type_, nalu.size); + + Ok(Slice { header: hdr, nalu }) + } + + /// Returns a previously parsed vps given `vps_id`, if any. + pub fn get_vps(&self, vps_id: u8) -> Option<&Rc> { + self.active_vpses.get(&vps_id) + } + + /// Returns a previously parsed sps given `sps_id`, if any. + pub fn get_sps(&self, sps_id: u8) -> Option<&Rc> { + self.active_spses.get(&sps_id) + } + + /// Returns a previously parsed pps given `pps_id`, if any. + pub fn get_pps(&self, pps_id: u8) -> Option<&Rc> { + self.active_ppses.get(&pps_id) + } +} + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + use crate::codec::h264::nalu::Nalu; + use crate::codec::h265::parser::Level; + use crate::codec::h265::parser::NaluHeader; + use crate::codec::h265::parser::NaluType; + use crate::codec::h265::parser::Parser; + use crate::codec::h265::parser::SliceType; + + const STREAM_BEAR: &[u8] = include_bytes!("test_data/bear.h265"); + const STREAM_BEAR_NUM_NALUS: usize = 35; + + const STREAM_BBB: &[u8] = include_bytes!("test_data/bbb.h265"); + const STREAM_BBB_NUM_NALUS: usize = 64; + + const STREAM_TEST25FPS: &[u8] = include_bytes!("test_data/test-25fps.h265"); + const STREAM_TEST25FPS_NUM_NALUS: usize = 254; + + const STREAM_TEST_25_FPS_SLICE_0: &[u8] = + include_bytes!("test_data/test-25fps-h265-slice-data-0.bin"); + const STREAM_TEST_25_FPS_SLICE_1: &[u8] = + include_bytes!("test_data/test-25fps-h265-slice-data-1.bin"); + + fn dispatch_parse_call(parser: &mut Parser, nalu: Nalu) -> Result<(), String> { + match nalu.header.type_ { + NaluType::TrailN + | NaluType::TrailR + | NaluType::TsaN + | NaluType::TsaR + | NaluType::StsaN + | NaluType::StsaR + | NaluType::RadlN + | NaluType::RadlR + | NaluType::RaslN + | NaluType::RaslR + | NaluType::BlaWLp + | NaluType::BlaWRadl + | NaluType::BlaNLp + | NaluType::IdrWRadl + | NaluType::IdrNLp + | NaluType::CraNut => { + parser.parse_slice_header(nalu).unwrap(); + } + NaluType::VpsNut => { + parser.parse_vps(&nalu).unwrap(); + } + NaluType::SpsNut => { + parser.parse_sps(&nalu).unwrap(); + } + NaluType::PpsNut => { + parser.parse_pps(&nalu).unwrap(); + } + _ => { /* ignore */ } + } + Ok(()) + } + + fn find_nalu_by_type( + bitstream: &[u8], + nalu_type: NaluType, + mut nskip: i32, + ) -> Option> { + let mut cursor = Cursor::new(bitstream); + while let Ok(nalu) = Nalu::::next(&mut cursor) { + if nalu.header.type_ == nalu_type { + if nskip == 0 { + return Some(nalu); + } else { + nskip -= 1; + } + } + } + + None + } + + /// This test is adapted from chromium, available at media/video/h265_parser_unittest.cc + #[test] + fn parse_nalus_from_stream_file() { + let mut cursor = Cursor::new(STREAM_BEAR); + let mut num_nalus = 0; + while Nalu::::next(&mut cursor).is_ok() { + num_nalus += 1; + } + + assert_eq!(num_nalus, STREAM_BEAR_NUM_NALUS); + + let mut cursor = Cursor::new(STREAM_BBB); + let mut num_nalus = 0; + while Nalu::::next(&mut cursor).is_ok() { + num_nalus += 1; + } + + assert_eq!(num_nalus, STREAM_BBB_NUM_NALUS); + + let mut cursor = Cursor::new(STREAM_TEST25FPS); + let mut num_nalus = 0; + while Nalu::::next(&mut cursor).is_ok() { + num_nalus += 1; + } + + assert_eq!(num_nalus, STREAM_TEST25FPS_NUM_NALUS); + } + + /// Parse the syntax, making sure we can parse the files without crashing. + /// Does not check whether the parsed values are correct. + #[test] + fn parse_syntax_from_nals() { + let mut cursor = Cursor::new(STREAM_BBB); + let mut parser = Parser::default(); + + while let Ok(nalu) = Nalu::::next(&mut cursor) { + dispatch_parse_call(&mut parser, nalu).unwrap(); + } + + let mut cursor = Cursor::new(STREAM_BEAR); + let mut parser = Parser::default(); + + while let Ok(nalu) = Nalu::::next(&mut cursor) { + dispatch_parse_call(&mut parser, nalu).unwrap(); + } + + let mut cursor = Cursor::new(STREAM_TEST25FPS); + let mut parser = Parser::default(); + + while let Ok(nalu) = Nalu::::next(&mut cursor) { + dispatch_parse_call(&mut parser, nalu).unwrap(); + } + } + + /// Adapted from Chromium (media/video/h265_parser_unittest.cc::VpsParsing()) + #[test] + fn chromium_vps_parsing() { + let mut cursor = Cursor::new(STREAM_BEAR); + let mut parser = Parser::default(); + + let vps_nalu = Nalu::::next(&mut cursor).unwrap(); + let vps = parser.parse_vps(&vps_nalu).unwrap(); + + assert!(vps.base_layer_internal_flag); + assert!(vps.base_layer_available_flag); + assert_eq!(vps.max_layers_minus1, 0); + assert_eq!(vps.max_sub_layers_minus1, 0); + assert!(vps.temporal_id_nesting_flag); + assert_eq!(vps.profile_tier_level.general_profile_idc, 1); + assert_eq!(vps.profile_tier_level.general_level_idc, Level::L2); + assert_eq!(vps.max_dec_pic_buffering_minus1[0], 4); + assert_eq!(vps.max_num_reorder_pics[0], 2); + assert_eq!(vps.max_latency_increase_plus1[0], 0); + for i in 1..7 { + assert_eq!(vps.max_dec_pic_buffering_minus1[i], 0); + assert_eq!(vps.max_num_reorder_pics[i], 0); + assert_eq!(vps.max_latency_increase_plus1[i], 0); + } + assert_eq!(vps.max_layer_id, 0); + assert_eq!(vps.num_layer_sets_minus1, 0); + assert!(!vps.timing_info_present_flag); + } + + /// Adapted from Chromium (media/video/h265_parser_unittest.cc::SpsParsing()) + #[test] + fn chromium_sps_parsing() { + let mut parser = Parser::default(); + let sps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::SpsNut, 0).unwrap(); + let sps = parser.parse_sps(&sps_nalu).unwrap(); + + assert_eq!(sps.max_sub_layers_minus1, 0); + assert_eq!(sps.profile_tier_level.general_profile_idc, 1); + assert_eq!(sps.profile_tier_level.general_level_idc, Level::L2); + assert_eq!(sps.seq_parameter_set_id, 0); + assert_eq!(sps.chroma_format_idc, 1); + assert!(!sps.separate_colour_plane_flag); + assert_eq!(sps.pic_width_in_luma_samples, 320); + assert_eq!(sps.pic_height_in_luma_samples, 184); + assert_eq!(sps.conf_win_left_offset, 0); + assert_eq!(sps.conf_win_right_offset, 0); + assert_eq!(sps.conf_win_top_offset, 0); + assert_eq!(sps.conf_win_bottom_offset, 2); + assert_eq!(sps.bit_depth_luma_minus8, 0); + assert_eq!(sps.bit_depth_chroma_minus8, 0); + assert_eq!(sps.log2_max_pic_order_cnt_lsb_minus4, 4); + assert_eq!(sps.max_dec_pic_buffering_minus1[0], 4); + assert_eq!(sps.max_num_reorder_pics[0], 2); + assert_eq!(sps.max_latency_increase_plus1[0], 0); + for i in 1..7 { + assert_eq!(sps.max_dec_pic_buffering_minus1[i], 0); + assert_eq!(sps.max_num_reorder_pics[i], 0); + assert_eq!(sps.max_latency_increase_plus1[i], 0); + } + assert_eq!(sps.log2_min_luma_coding_block_size_minus3, 0); + assert_eq!(sps.log2_diff_max_min_luma_coding_block_size, 3); + assert_eq!(sps.log2_min_luma_transform_block_size_minus2, 0); + assert_eq!(sps.log2_diff_max_min_luma_transform_block_size, 3); + assert_eq!(sps.max_transform_hierarchy_depth_inter, 0); + assert_eq!(sps.max_transform_hierarchy_depth_intra, 0); + assert!(!sps.scaling_list_enabled_flag); + assert!(!sps.scaling_list_data_present_flag); + assert!(!sps.amp_enabled_flag); + assert!(sps.sample_adaptive_offset_enabled_flag); + assert!(!sps.pcm_enabled_flag); + assert_eq!(sps.pcm_sample_bit_depth_luma_minus1, 0); + assert_eq!(sps.pcm_sample_bit_depth_chroma_minus1, 0); + assert_eq!(sps.log2_min_pcm_luma_coding_block_size_minus3, 0); + assert_eq!(sps.log2_diff_max_min_pcm_luma_coding_block_size, 0); + assert!(!sps.pcm_loop_filter_disabled_flag); + assert_eq!(sps.num_short_term_ref_pic_sets, 0); + assert_eq!(sps.num_long_term_ref_pics_sps, 0); + assert!(sps.temporal_mvp_enabled_flag); + assert!(sps.strong_intra_smoothing_enabled_flag); + assert_eq!(sps.vui_parameters.sar_width, 0); + assert_eq!(sps.vui_parameters.sar_height, 0); + assert!(!sps.vui_parameters.video_full_range_flag); + assert!(!sps.vui_parameters.colour_description_present_flag); + + // Note: the original test has 0 for the three variables below, but they + // have valid defaults in the spec (i.e.: 2). + assert_eq!(sps.vui_parameters.colour_primaries, 2); + assert_eq!(sps.vui_parameters.transfer_characteristics, 2); + assert_eq!(sps.vui_parameters.matrix_coeffs, 2); + + assert_eq!(sps.vui_parameters.def_disp_win_left_offset, 0); + assert_eq!(sps.vui_parameters.def_disp_win_right_offset, 0); + assert_eq!(sps.vui_parameters.def_disp_win_top_offset, 0); + assert_eq!(sps.vui_parameters.def_disp_win_bottom_offset, 0); + } + + /// Adapted from Chromium (media/video/h265_parser_unittest.cc::PpsParsing()) + #[test] + fn chromium_pps_parsing() { + let mut parser = Parser::default(); + + // Have to parse the SPS to set up the parser's internal state. + let sps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::SpsNut, 0).unwrap(); + parser.parse_sps(&sps_nalu).unwrap(); + + let pps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::PpsNut, 0).unwrap(); + let pps = parser.parse_pps(&pps_nalu).unwrap(); + + assert_eq!(pps.pic_parameter_set_id, 0); + assert_eq!(pps.seq_parameter_set_id, 0); + assert!(!pps.dependent_slice_segments_enabled_flag); + assert!(!pps.output_flag_present_flag); + assert_eq!(pps.num_extra_slice_header_bits, 0); + assert!(pps.sign_data_hiding_enabled_flag); + assert!(!pps.cabac_init_present_flag); + assert_eq!(pps.num_ref_idx_l0_default_active_minus1, 0); + assert_eq!(pps.num_ref_idx_l1_default_active_minus1, 0); + assert_eq!(pps.init_qp_minus26, 0); + assert!(!pps.constrained_intra_pred_flag); + assert!(!pps.transform_skip_enabled_flag); + assert!(pps.cu_qp_delta_enabled_flag); + assert_eq!(pps.diff_cu_qp_delta_depth, 0); + assert_eq!(pps.cb_qp_offset, 0); + assert_eq!(pps.cr_qp_offset, 0); + assert!(!pps.slice_chroma_qp_offsets_present_flag); + assert!(pps.weighted_pred_flag); + assert!(!pps.weighted_bipred_flag); + assert!(!pps.transquant_bypass_enabled_flag); + assert!(!pps.tiles_enabled_flag); + assert!(pps.entropy_coding_sync_enabled_flag); + assert!(pps.loop_filter_across_tiles_enabled_flag); + assert!(!pps.scaling_list_data_present_flag); + assert!(!pps.lists_modification_present_flag); + assert_eq!(pps.log2_parallel_merge_level_minus2, 0); + assert!(!pps.slice_segment_header_extension_present_flag); + } + + /// Adapted from Chromium (media/video/h265_parser_unittest.cc::SliceHeaderParsing()) + #[test] + fn chromium_slice_header_parsing() { + let mut parser = Parser::default(); + + // Have to parse the SPS/VPS/PPS to set up the parser's internal state. + let vps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::VpsNut, 0).unwrap(); + parser.parse_vps(&vps_nalu).unwrap(); + + let sps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::SpsNut, 0).unwrap(); + parser.parse_sps(&sps_nalu).unwrap(); + + let pps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::PpsNut, 0).unwrap(); + parser.parse_pps(&pps_nalu).unwrap(); + + // Just like the Chromium test, do an IDR slice, then a non IDR slice. + let slice_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::IdrWRadl, 0).unwrap(); + let slice = parser.parse_slice_header(slice_nalu).unwrap(); + let hdr = &slice.header; + assert!(hdr.first_slice_segment_in_pic_flag); + assert!(!hdr.no_output_of_prior_pics_flag); + assert_eq!(hdr.pic_parameter_set_id, 0); + assert!(!hdr.dependent_slice_segment_flag); + assert_eq!(hdr.type_, SliceType::I); + assert!(hdr.sao_luma_flag); + assert!(hdr.sao_chroma_flag); + assert_eq!(hdr.qp_delta, 8); + assert!(hdr.loop_filter_across_slices_enabled_flag); + + let slice_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::TrailR, 0).unwrap(); + let slice = parser.parse_slice_header(slice_nalu).unwrap(); + let hdr = &slice.header; + assert!(hdr.first_slice_segment_in_pic_flag); + assert_eq!(hdr.pic_parameter_set_id, 0); + assert!(!hdr.dependent_slice_segment_flag); + assert_eq!(hdr.type_, SliceType::P); + assert_eq!(hdr.pic_order_cnt_lsb, 4); + assert!(!hdr.short_term_ref_pic_set_sps_flag); + assert_eq!(hdr.short_term_ref_pic_set.num_negative_pics, 1); + assert_eq!(hdr.short_term_ref_pic_set.num_positive_pics, 0); + assert_eq!(hdr.short_term_ref_pic_set.delta_poc_s0[0], -4); + assert!(hdr.short_term_ref_pic_set.used_by_curr_pic_s0[0]); + assert!(hdr.temporal_mvp_enabled_flag); + assert!(hdr.sao_luma_flag); + assert!(hdr.sao_chroma_flag); + assert!(!hdr.num_ref_idx_active_override_flag); + assert_eq!(hdr.pred_weight_table.luma_log2_weight_denom, 0); + assert_eq!(hdr.pred_weight_table.delta_chroma_log2_weight_denom, 7); + assert_eq!(hdr.pred_weight_table.delta_luma_weight_l0[0], 0); + assert_eq!(hdr.pred_weight_table.luma_offset_l0[0], -2); + assert_eq!(hdr.pred_weight_table.delta_chroma_weight_l0[0][0], -9); + assert_eq!(hdr.pred_weight_table.delta_chroma_weight_l0[0][1], -9); + assert_eq!(hdr.pred_weight_table.delta_chroma_offset_l0[0][0], 0); + assert_eq!(hdr.pred_weight_table.delta_chroma_offset_l0[0][1], 0); + assert_eq!(hdr.five_minus_max_num_merge_cand, 3); + assert_eq!(hdr.qp_delta, 8); + assert!(hdr.loop_filter_across_slices_enabled_flag); + } + + /// A custom test for VPS parsing with data manually extracted from + /// GStreamer using GDB. + #[test] + fn test25fps_vps_header_parsing() { + let mut cursor = Cursor::new(STREAM_TEST25FPS); + let mut parser = Parser::default(); + + let vps_nalu = Nalu::::next(&mut cursor).unwrap(); + let vps = parser.parse_vps(&vps_nalu).unwrap(); + assert!(vps.base_layer_internal_flag); + assert!(vps.base_layer_available_flag); + assert_eq!(vps.max_layers_minus1, 0); + assert_eq!(vps.max_sub_layers_minus1, 0); + assert!(vps.temporal_id_nesting_flag); + assert_eq!(vps.profile_tier_level.general_profile_space, 0); + assert!(!vps.profile_tier_level.general_tier_flag); + assert_eq!(vps.profile_tier_level.general_profile_idc, 1); + for i in 0..32 { + let val = i == 1 || i == 2; + assert_eq!(vps.profile_tier_level.general_profile_compatibility_flag[i], val); + } + assert!(vps.profile_tier_level.general_progressive_source_flag); + assert!(!vps.profile_tier_level.general_interlaced_source_flag); + assert!(!vps.profile_tier_level.general_non_packed_constraint_flag,); + assert!(vps.profile_tier_level.general_frame_only_constraint_flag,); + assert!(!vps.profile_tier_level.general_max_12bit_constraint_flag,); + assert!(!vps.profile_tier_level.general_max_10bit_constraint_flag,); + assert!(!vps.profile_tier_level.general_max_8bit_constraint_flag,); + assert!(!vps.profile_tier_level.general_max_422chroma_constraint_flag,); + assert!(!vps.profile_tier_level.general_max_420chroma_constraint_flag,); + assert!(!vps.profile_tier_level.general_max_monochrome_constraint_flag,); + assert!(!vps.profile_tier_level.general_intra_constraint_flag); + assert!(!vps.profile_tier_level.general_one_picture_only_constraint_flag,); + assert!(!vps.profile_tier_level.general_lower_bit_rate_constraint_flag,); + assert!(!vps.profile_tier_level.general_max_14bit_constraint_flag,); + assert_eq!(vps.profile_tier_level.general_level_idc, Level::L2); + + assert!(vps.sub_layer_ordering_info_present_flag); + assert_eq!(vps.max_dec_pic_buffering_minus1[0], 4); + assert_eq!(vps.max_num_reorder_pics[0], 2); + assert_eq!(vps.max_latency_increase_plus1[0], 5); + for i in 1..7 { + assert_eq!(vps.max_dec_pic_buffering_minus1[i], 0); + assert_eq!(vps.max_num_reorder_pics[i], 0); + assert_eq!(vps.max_latency_increase_plus1[i], 0); + } + + assert_eq!(vps.max_layer_id, 0); + assert_eq!(vps.num_layer_sets_minus1, 0); + assert!(!vps.timing_info_present_flag); + assert_eq!(vps.num_units_in_tick, 0); + assert_eq!(vps.time_scale, 0); + assert!(!vps.poc_proportional_to_timing_flag); + assert_eq!(vps.num_ticks_poc_diff_one_minus1, 0); + assert_eq!(vps.num_hrd_parameters, 0); + } + + /// A custom test for SPS parsing with data manually extracted from + /// GStreamer using GDB. + #[test] + fn test25fps_sps_header_parsing() { + let mut parser = Parser::default(); + + let sps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::SpsNut, 0).unwrap(); + let sps = parser.parse_sps(&sps_nalu).unwrap(); + + assert_eq!(sps.max_sub_layers_minus1, 0); + + assert_eq!(sps.profile_tier_level.general_profile_space, 0); + assert!(!sps.profile_tier_level.general_tier_flag); + assert_eq!(sps.profile_tier_level.general_profile_idc, 1); + for i in 0..32 { + let val = i == 1 || i == 2; + assert_eq!(sps.profile_tier_level.general_profile_compatibility_flag[i], val); + } + assert!(sps.profile_tier_level.general_progressive_source_flag); + assert!(!sps.profile_tier_level.general_interlaced_source_flag); + assert!(!sps.profile_tier_level.general_non_packed_constraint_flag,); + assert!(sps.profile_tier_level.general_frame_only_constraint_flag,); + assert!(!sps.profile_tier_level.general_max_12bit_constraint_flag,); + assert!(!sps.profile_tier_level.general_max_10bit_constraint_flag,); + assert!(!sps.profile_tier_level.general_max_8bit_constraint_flag,); + assert!(!sps.profile_tier_level.general_max_422chroma_constraint_flag,); + assert!(!sps.profile_tier_level.general_max_420chroma_constraint_flag,); + assert!(!sps.profile_tier_level.general_max_monochrome_constraint_flag,); + assert!(!sps.profile_tier_level.general_intra_constraint_flag); + assert!(!sps.profile_tier_level.general_one_picture_only_constraint_flag,); + assert!(!sps.profile_tier_level.general_lower_bit_rate_constraint_flag,); + assert!(!sps.profile_tier_level.general_max_14bit_constraint_flag,); + assert_eq!(sps.profile_tier_level.general_level_idc, Level::L2); + + assert_eq!(sps.seq_parameter_set_id, 0); + assert_eq!(sps.chroma_format_idc, 1); + assert!(!sps.separate_colour_plane_flag); + assert_eq!(sps.pic_width_in_luma_samples, 320); + assert_eq!(sps.pic_height_in_luma_samples, 240); + assert_eq!(sps.conf_win_left_offset, 0); + assert_eq!(sps.conf_win_right_offset, 0); + assert_eq!(sps.conf_win_top_offset, 0); + assert_eq!(sps.conf_win_bottom_offset, 0); + assert_eq!(sps.bit_depth_luma_minus8, 0); + assert_eq!(sps.bit_depth_chroma_minus8, 0); + assert_eq!(sps.log2_max_pic_order_cnt_lsb_minus4, 4); + assert!(sps.sub_layer_ordering_info_present_flag); + assert_eq!(sps.max_dec_pic_buffering_minus1[0], 4); + assert_eq!(sps.max_num_reorder_pics[0], 2); + assert_eq!(sps.max_latency_increase_plus1[0], 5); + for i in 1..7 { + assert_eq!(sps.max_dec_pic_buffering_minus1[i], 0); + assert_eq!(sps.max_num_reorder_pics[i], 0); + assert_eq!(sps.max_latency_increase_plus1[i], 0); + } + assert_eq!(sps.log2_min_luma_coding_block_size_minus3, 0); + assert_eq!(sps.log2_diff_max_min_luma_coding_block_size, 3); + assert_eq!(sps.log2_min_luma_transform_block_size_minus2, 0); + assert_eq!(sps.log2_diff_max_min_luma_transform_block_size, 3); + assert_eq!(sps.max_transform_hierarchy_depth_inter, 0); + assert_eq!(sps.max_transform_hierarchy_depth_intra, 0); + assert!(!sps.scaling_list_enabled_flag); + assert!(!sps.scaling_list_data_present_flag); + assert!(!sps.amp_enabled_flag); + assert!(sps.sample_adaptive_offset_enabled_flag); + assert!(!sps.pcm_enabled_flag); + assert_eq!(sps.pcm_sample_bit_depth_luma_minus1, 0); + assert_eq!(sps.pcm_sample_bit_depth_chroma_minus1, 0); + assert_eq!(sps.log2_min_pcm_luma_coding_block_size_minus3, 0); + assert_eq!(sps.log2_diff_max_min_pcm_luma_coding_block_size, 0); + assert!(!sps.pcm_loop_filter_disabled_flag); + assert_eq!(sps.num_short_term_ref_pic_sets, 0); + assert_eq!(sps.num_long_term_ref_pics_sps, 0); + assert!(sps.temporal_mvp_enabled_flag); + assert!(sps.strong_intra_smoothing_enabled_flag); + assert_eq!(sps.vui_parameters.sar_width, 0); + assert_eq!(sps.vui_parameters.sar_height, 0); + assert!(!sps.vui_parameters.video_full_range_flag); + assert!(!sps.vui_parameters.colour_description_present_flag); + assert!(sps.vui_parameters.video_signal_type_present_flag); + assert!(sps.vui_parameters.timing_info_present_flag); + assert_eq!(sps.vui_parameters.num_units_in_tick, 1); + assert_eq!(sps.vui_parameters.time_scale, 25); + assert!(!sps.vui_parameters.poc_proportional_to_timing_flag); + assert_eq!(sps.vui_parameters.num_ticks_poc_diff_one_minus1, 0); + assert!(!sps.vui_parameters.hrd_parameters_present_flag); + assert_eq!(sps.vui_parameters.colour_primaries, 2); + assert_eq!(sps.vui_parameters.transfer_characteristics, 2); + assert_eq!(sps.vui_parameters.matrix_coeffs, 2); + assert_eq!(sps.vui_parameters.def_disp_win_left_offset, 0); + assert_eq!(sps.vui_parameters.def_disp_win_right_offset, 0); + assert_eq!(sps.vui_parameters.def_disp_win_top_offset, 0); + assert_eq!(sps.vui_parameters.def_disp_win_bottom_offset, 0); + } + + /// A custom test for PPS parsing with data manually extracted from + /// GStreamer using GDB. + #[test] + fn test25fps_pps_header_parsing() { + let mut parser = Parser::default(); + + let sps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::SpsNut, 0).unwrap(); + parser.parse_sps(&sps_nalu).unwrap(); + + let pps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::PpsNut, 0).unwrap(); + let pps = parser.parse_pps(&pps_nalu).unwrap(); + + assert!(!pps.dependent_slice_segments_enabled_flag); + assert!(!pps.output_flag_present_flag); + assert_eq!(pps.num_extra_slice_header_bits, 0); + assert!(pps.sign_data_hiding_enabled_flag); + assert!(!pps.cabac_init_present_flag); + assert_eq!(pps.num_ref_idx_l0_default_active_minus1, 0); + assert_eq!(pps.num_ref_idx_l1_default_active_minus1, 0); + assert_eq!(pps.init_qp_minus26, 0); + assert!(!pps.constrained_intra_pred_flag); + assert!(!pps.transform_skip_enabled_flag); + assert!(pps.cu_qp_delta_enabled_flag); + assert_eq!(pps.diff_cu_qp_delta_depth, 1); + assert_eq!(pps.cb_qp_offset, 0); + assert_eq!(pps.cr_qp_offset, 0); + assert!(!pps.slice_chroma_qp_offsets_present_flag); + assert!(pps.weighted_pred_flag); + assert!(!pps.weighted_bipred_flag); + assert!(!pps.transquant_bypass_enabled_flag); + assert!(!pps.tiles_enabled_flag); + assert!(pps.entropy_coding_sync_enabled_flag); + assert_eq!(pps.num_tile_rows_minus1, 0); + assert_eq!(pps.num_tile_columns_minus1, 0); + assert!(pps.uniform_spacing_flag); + assert_eq!(pps.column_width_minus1, [0; 19]); + assert_eq!(pps.row_height_minus1, [0; 21]); + assert!(pps.loop_filter_across_slices_enabled_flag); + assert!(pps.loop_filter_across_tiles_enabled_flag); + assert!(!pps.deblocking_filter_control_present_flag); + assert!(!pps.deblocking_filter_override_enabled_flag); + assert!(!pps.deblocking_filter_disabled_flag); + assert_eq!(pps.beta_offset_div2, 0); + assert_eq!(pps.tc_offset_div2, 0); + assert!(!pps.lists_modification_present_flag); + assert_eq!(pps.log2_parallel_merge_level_minus2, 0); + assert!(!pps.slice_segment_header_extension_present_flag); + assert!(!pps.extension_present_flag); + } + + /// A custom test for slice header parsing with data manually extracted from + /// GStreamer using GDB. + #[test] + fn test25fps_slice_header_parsing() { + let mut parser = Parser::default(); + + // Have to parse the SPS/VPS/PPS to set up the parser's internal state. + let vps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::VpsNut, 0).unwrap(); + parser.parse_vps(&vps_nalu).unwrap(); + + let sps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::SpsNut, 0).unwrap(); + parser.parse_sps(&sps_nalu).unwrap(); + + let pps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::PpsNut, 0).unwrap(); + parser.parse_pps(&pps_nalu).unwrap(); + + let slice_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::IdrNLp, 0).unwrap(); + let slice = parser.parse_slice_header(slice_nalu).unwrap(); + let hdr = &slice.header; + + assert!(hdr.first_slice_segment_in_pic_flag); + assert!(!hdr.no_output_of_prior_pics_flag); + assert!(!hdr.dependent_slice_segment_flag); + assert_eq!(hdr.type_, SliceType::I); + assert!(hdr.pic_output_flag); + assert_eq!(hdr.colour_plane_id, 0); + assert_eq!(hdr.pic_order_cnt_lsb, 0); + assert!(!hdr.short_term_ref_pic_set_sps_flag); + assert_eq!(hdr.lt_idx_sps, [0; 16]); + assert_eq!(hdr.poc_lsb_lt, [0; 16]); + assert_eq!(hdr.used_by_curr_pic_lt, [false; 16]); + assert_eq!(hdr.delta_poc_msb_cycle_lt, [0; 16]); + assert_eq!(hdr.delta_poc_msb_present_flag, [false; 16]); + assert!(!hdr.temporal_mvp_enabled_flag); + assert!(hdr.sao_luma_flag); + assert!(hdr.sao_chroma_flag); + assert!(!hdr.num_ref_idx_active_override_flag); + assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); + assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); + assert!(!hdr.cabac_init_flag); + assert!(hdr.collocated_from_l0_flag); + assert_eq!(hdr.five_minus_max_num_merge_cand, 0); + assert!(!hdr.use_integer_mv_flag); + assert_eq!(hdr.qp_delta, 7); + assert_eq!(hdr.cb_qp_offset, 0); + assert_eq!(hdr.cr_qp_offset, 0); + assert!(!hdr.cu_chroma_qp_offset_enabled_flag); + assert!(!hdr.deblocking_filter_override_flag); + assert!(!hdr.deblocking_filter_override_flag); + assert_eq!(hdr.beta_offset_div2, 0); + assert_eq!(hdr.tc_offset_div2, 0); + assert!(hdr.loop_filter_across_slices_enabled_flag); + assert_eq!(hdr.num_entry_point_offsets, 3); + assert_eq!(hdr.offset_len_minus1, 11); + assert_eq!(hdr.num_pic_total_curr, 0); + + // Remove the 2 bytes from the NALU header. + assert_eq!(hdr.header_bit_size - 16, 72); + + assert_eq!(hdr.n_emulation_prevention_bytes, 0); + + assert_eq!(slice.nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_0); + + // Next slice + let slice_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::TrailR, 0).unwrap(); + let slice = parser.parse_slice_header(slice_nalu).unwrap(); + let hdr = &slice.header; + + assert!(hdr.first_slice_segment_in_pic_flag); + assert!(!hdr.no_output_of_prior_pics_flag); + assert!(!hdr.dependent_slice_segment_flag); + assert_eq!(hdr.type_, SliceType::P); + assert!(hdr.pic_output_flag); + assert_eq!(hdr.colour_plane_id, 0); + assert_eq!(hdr.pic_order_cnt_lsb, 3); + assert!(!hdr.short_term_ref_pic_set_sps_flag); + assert_eq!(hdr.short_term_ref_pic_set.num_delta_pocs, 1); + assert_eq!(hdr.short_term_ref_pic_set.num_negative_pics, 1); + assert_eq!(hdr.short_term_ref_pic_set.num_positive_pics, 0); + assert!(hdr.short_term_ref_pic_set.used_by_curr_pic_s0[0]); + assert_eq!(hdr.short_term_ref_pic_set.delta_poc_s0[0], -3); + assert_eq!(hdr.lt_idx_sps, [0; 16]); + assert_eq!(hdr.poc_lsb_lt, [0; 16]); + assert_eq!(hdr.used_by_curr_pic_lt, [false; 16]); + assert_eq!(hdr.delta_poc_msb_cycle_lt, [0; 16]); + assert_eq!(hdr.delta_poc_msb_present_flag, [false; 16]); + assert!(hdr.temporal_mvp_enabled_flag); + assert!(hdr.sao_luma_flag); + assert!(hdr.sao_chroma_flag); + assert!(!hdr.num_ref_idx_active_override_flag); + assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); + assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); + assert!(!hdr.cabac_init_flag); + assert!(hdr.collocated_from_l0_flag); + assert_eq!(hdr.pred_weight_table.luma_log2_weight_denom, 7); + assert_eq!(hdr.five_minus_max_num_merge_cand, 2); + assert!(!hdr.use_integer_mv_flag); + assert_eq!(hdr.num_entry_point_offsets, 3); + assert_eq!(hdr.qp_delta, 7); + assert_eq!(hdr.cb_qp_offset, 0); + assert_eq!(hdr.cr_qp_offset, 0); + assert!(!hdr.cu_chroma_qp_offset_enabled_flag); + assert!(!hdr.deblocking_filter_override_flag); + assert!(!hdr.deblocking_filter_override_flag); + assert_eq!(hdr.beta_offset_div2, 0); + assert_eq!(hdr.tc_offset_div2, 0); + assert!(!hdr.loop_filter_across_slices_enabled_flag); + assert_eq!(hdr.num_entry_point_offsets, 3); + assert_eq!(hdr.offset_len_minus1, 10); + assert_eq!(hdr.num_pic_total_curr, 1); + + assert_eq!(slice.nalu.size, 2983); + // Subtract 2 bytes to account for the header size. + assert_eq!(hdr.header_bit_size - 16, 96); + assert_eq!(slice.nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_1); + + // Next slice + let slice_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::TrailR, 1).unwrap(); + let slice = parser.parse_slice_header(slice_nalu).unwrap(); + let hdr = &slice.header; + + assert_eq!(slice.nalu.size, 290); + // Subtract 2 bytes to account for the header size. + assert_eq!(hdr.header_bit_size - 16, 80); + } +} diff --git a/vendor/cros-codecs/src/codec/h265/picture.rs b/vendor/cros-codecs/src/codec/h265/picture.rs new file mode 100644 index 00000000..ecb86f2c --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/picture.rs @@ -0,0 +1,154 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::codec::h265::parser::NaluType; +use crate::codec::h265::parser::Slice; + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum Reference { + #[default] + None, + ShortTerm, + LongTerm, +} + +/// Data associated with an h.265 picture. Most fields are extracted from the +/// slice header and kept for future processing. +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct PictureData { + // Fields extracted from the slice header. These are the CamelCase + // variables, unless noted otherwise. + pub nalu_type: NaluType, + pub no_rasl_output_flag: bool, + pub pic_output_flag: bool, + pub valid_for_prev_tid0_pic: bool, + pub slice_pic_order_cnt_lsb: i32, + pub pic_order_cnt_msb: i32, + pub pic_order_cnt_val: i32, + pub no_output_of_prior_pics_flag: bool, + + // Internal state. + pub first_picture_after_eos: bool, + reference: Reference, + pub pic_latency_cnt: i32, + pub needed_for_output: bool, + pub short_term_ref_pic_set_size_bits: u32, +} + +impl PictureData { + /// Instantiates a new `PictureData` from a slice. + /// + /// See 8.1.3 Decoding process for a coded picture with nuh_layer_id equal + /// to 0. + /// + /// This will also call the picture order count process (clause 8.3.1) to + /// correctly initialize the POC values. + pub fn new_from_slice( + slice: &Slice, + first_picture_in_bitstream: bool, + first_picture_after_eos: bool, + prev_tid0_pic: Option<&PictureData>, + max_pic_order_cnt_lsb: i32, + ) -> Self { + let hdr = &slice.header; + let nalu_type = slice.nalu.header.type_; + + // We assume HandleCraAsBlafFLag == 0, as it is only set through + // external means, which we do not provide. + + let mut pic_order_cnt_msb = 0; + let slice_pic_order_cnt_lsb: i32 = hdr.pic_order_cnt_lsb.into(); + + // Compute the output flags: + // The value of NoRaslOutputFlag is equal to 1 for each IDR access + // unit, each BLA access unit, and each CRA access unit that is the + // first access unit in the bitstream in decoding order, is the first + // access unit that follows an end of sequence NAL unit in decoding + // order, or has HandleCraAsBlaFlag equal to 1. + let no_rasl_output_flag = nalu_type.is_idr() + || nalu_type.is_bla() + || (nalu_type.is_cra() && first_picture_in_bitstream) + || first_picture_after_eos; + + let pic_output_flag = if slice.nalu.header.type_.is_rasl() && no_rasl_output_flag { + false + } else { + hdr.pic_output_flag + }; + + // Compute the Picture Order Count. See 8.3.1 Decoding Process for + // Picture Order Count + if !(nalu_type.is_irap() && no_rasl_output_flag) { + if let Some(prev_tid0_pic) = prev_tid0_pic { + // Equation (8-1) + let prev_pic_order_cnt_lsb = prev_tid0_pic.slice_pic_order_cnt_lsb; + let prev_pic_order_cnt_msb = prev_tid0_pic.pic_order_cnt_msb; + if (slice_pic_order_cnt_lsb < prev_pic_order_cnt_lsb) + && (prev_pic_order_cnt_lsb - slice_pic_order_cnt_lsb) + >= (max_pic_order_cnt_lsb / 2) + { + pic_order_cnt_msb = prev_pic_order_cnt_msb + max_pic_order_cnt_lsb; + } else if (slice_pic_order_cnt_lsb > prev_pic_order_cnt_lsb) + && (slice_pic_order_cnt_lsb - prev_pic_order_cnt_lsb) + > (max_pic_order_cnt_lsb / 2) + { + pic_order_cnt_msb = prev_pic_order_cnt_msb - max_pic_order_cnt_lsb; + } else { + pic_order_cnt_msb = prev_pic_order_cnt_msb; + } + } + } + + // Compute whether this picture will be a valid prevTid0Pic, i.e.: + // + // Let prevTid0Pic be the previous picture in decoding order that has + // TemporalId equal to 0 and that is not a RASL, RADL or SLNR picture. + // + // Use this flag to correctly set up the field in the decoder during + // `finish_picture`. + let valid_for_prev_tid0_pic = slice.nalu.header.nuh_temporal_id() == 0 + && !nalu_type.is_radl() + && !nalu_type.is_rasl() + && !nalu_type.is_slnr(); + + let no_output_of_prior_pics_flag = + if nalu_type.is_irap() && no_rasl_output_flag && !first_picture_in_bitstream { + nalu_type.is_cra() || hdr.no_output_of_prior_pics_flag + } else { + false + }; + + Self { + nalu_type, + no_rasl_output_flag, + no_output_of_prior_pics_flag, + pic_output_flag, + valid_for_prev_tid0_pic, + slice_pic_order_cnt_lsb, + pic_order_cnt_msb, + // Equation (8-2) + pic_order_cnt_val: pic_order_cnt_msb + slice_pic_order_cnt_lsb, + first_picture_after_eos, + reference: Default::default(), + pic_latency_cnt: 0, + needed_for_output: false, + short_term_ref_pic_set_size_bits: hdr.st_rps_bits, + } + } + + /// Whether the current picture is a reference, either ShortTerm or LongTerm. + pub fn is_ref(&self) -> bool { + !matches!(self.reference, Reference::None) + } + + pub fn set_reference(&mut self, reference: Reference) { + log::debug!("Set reference of POC {} to {:?}", self.pic_order_cnt_val, reference); + + self.reference = reference; + } + + pub fn reference(&self) -> &Reference { + &self.reference + } +} diff --git a/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265 b/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265 new file mode 100644 index 0000000000000000000000000000000000000000..f3ca72ba839885f943be470e397e2743f523e848 GIT binary patch literal 4150 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 literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.crc b/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.crc new file mode 100644 index 00000000..f8e992b3 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.crc @@ -0,0 +1,3 @@ +2904d4d2 +d8e11777 +a1108fed diff --git a/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.md5 b/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.md5 new file mode 100644 index 00000000..3cab8425 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.md5 @@ -0,0 +1,3 @@ +c45648e1d3dd68913e998bd6ddb0f633 +8a44f604b2518c6caa7bb422907c8d17 +fe6ac1f24e248f3dba54087245380dd8 diff --git a/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265 b/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265 new file mode 100644 index 0000000000000000000000000000000000000000..50935e39435161993a7925d3f83b8a0ec349dd4f GIT binary patch literal 3808 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 G?*9RO^9&pS literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.crc b/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.crc new file mode 100644 index 00000000..a3c5b67f --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.crc @@ -0,0 +1,30 @@ +0a6ac0dd +350b4669 +61a8b9ad +f566d7c6 +8d8ab332 +52468dae +44bdbf5f +935e3db3 +814109bc +68dc2234 +d5b47d8b +852f3e1f +3f54c08b +9e33e4ae +047c4f0d +10cebb43 +87774650 +08adc4be +837797a6 +17f80256 +221e943f +e06ff206 +7fb43061 +e5dc2425 +37928778 +3a787cf0 +14ea1afd +0331b84f +bdfa1606 +28216ab7 diff --git a/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.md5 b/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.md5 new file mode 100644 index 00000000..4163d712 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.md5 @@ -0,0 +1,30 @@ +037e7ff0ab754f16dc254edbecaf5bc0 +47d9c4d6427726b74dd8586aeea147e4 +1fae73f309640f704f8358fc0bae4c39 +579c2fe2b1fa83ab3082e30daebba728 +a4e01659d6bc12626d445a02fa5f977e +3bcadc5d9985e44338edca617fd84541 +ac92b5ee1f79b5bda8f662eefab40085 +03d24eba6675f51496b9338d97c39308 +77f9d5f1e2e5bd40164336d9fe7970ef +355f2e0f8e8c4702ad45c790415004df +1d9563a8ccdf00b2116edb508b665e7e +44eb70aeba2c5eba6b0ce931d6fff3e5 +c5fab1da55bd8177342d4e174bdc1474 +30f0d9a37231141722ba65c61bd59d09 +a1e8fdc8b33af447e4486c17d028b706 +11cd37f438399a595745dff608965aa2 +8025ff7ac2d9c9cda4039900834c1182 +e934c10259100457b44bd13a8630a3e0 +9f745c40aae4b6c31d57a0a43b39a4d2 +041ea32d187a011baafda69c1f079b2e +d8a172fed23b9f0b4213bc7f8f1375a9 +22e9e048f8e04410307068b6bc2e79d7 +3c9d59aa49756060f8e78a5d608db8e6 +5482d7c76a6ff60c9c32fffc27c39873 +44a4d11b3ecdc827cc21ff732e1c7c7d +aeaaff7c5b23b4f03d939de6c12e5006 +afa6ece7a21db66eece97e2e0713f29c +d84b7794f4a4968ea7fadf5cba7333d0 +f9644eca3981a123b917938c7a4963fb +e02c049b9e516e10c05ce4469856e37d diff --git a/vendor/cros-codecs/src/codec/h265/test_data/gen_crcs.sh b/vendor/cros-codecs/src/codec/h265/test_data/gen_crcs.sh new file mode 100755 index 00000000..4c7db423 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/gen_crcs.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Generates the CRCs for all .h265 files in the current directory using ffmpeg. + +for f in `ls *.h265`; do + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5 +done diff --git a/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-0.bin b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-0.bin new file mode 100644 index 0000000000000000000000000000000000000000..b06651aa3e433444ac3d186c63da695d2dc050c0 GIT binary patch literal 8291 zcmV-pAe`SQ0k0h(%WOOxJ%FSd2nk^^x6teU=01Q=bff^($e2TWe*8A>0E4O$p#w)-*jgtC?w~>vO^R7{!1#SsXG~B?lbpSj&84X}f~aK7KK8BC+_P&G zKm!oWLs}L0SP+=hlf;HY5x7G&z-^@a8T!;~Dxcep{($>q}J)U&YWy0l|2xDy zm*=|)0)YFM-nyEs25AcTrJPqjJ@UbE8_{H2cCJu1`=jSFe{dSLUzvC@Yi(If|4_Ps z+fT`79ZMvL;EP8%mxCcho$nsv^^0^m9KZd9L=$$KSQtlDS;yo%$B;lI$k6&PW%({M zf+@qa-yR~}PcO8vx8HPRRg02cy@ok#yz zCU`2u)}vUkwOaz9s;e_K6f>{Yu`C6;7FLFUL&3Y{4ZQ64O`FsGltQfQX5QYjrKUyRj{IMs zsY*?R2OBUNeCTI?pk2d#nM&6jFL>=|do`Dv5eo;DE$mS;_moJl!3%q@KEfyvW~MME zRPR9N!tK`?7Q^GkSAhTunOfiMouWBGfC+NH!~%d^q~*S{Bk*-){xX@m>X*JV_ZF7U z%ta*y>9VaRT;loLhEQa%g-zq{qgLyd+(-b_9@+~Q*j?kJWS(XLG+hF5s60)d8)6hV zgRRT2YcKX}E?PuJ6K71d_5+_<$hRNvchTE5+w@{*f!wsD0cHZ3l8e zn1Un#)P>Sy?jMtd<$`BqP?ZV0Jaq}IElSXH^$Ns%;BXGio8y(h8av(^qO9Uscpf>N z&77qY&!S@G)E7RJI91b@aMhZ`V#{6?RNWHR6;5SX&G<{(M80AN0z!Eh;PulxYryBT z7<=8H3j(@P`kFp4|aE(0&1A^dfj^;-wz|OtCMHMH`v$YicDpH_J<< z-ms@)@k_q+KY4FtH1%1M6a#3&eB!KP1UIoDY?rztVOD_x9=q`GCWI7B2_$qwDF4bZ z4}Gl4$xBZ-y15_iOi;5qpJX7s_&sJtD{QzAg#DKxC)0;%nZ`DnZ=6q*WIDqzW<{ z9LQ)M?$fy@x`izawu~bfJdXpqr-!h zn=oEnemcjh6?N9T7pZNZHaO}9oQ8|t?KQB;o|!F%&um-g9sYtHv)r%1XZ=+?4ogQR z@Y@jWWD%KqmKtAoTn-3>a}D6{va)~*?lCD-?1k&?abQ8C5zg7roPYh$TV!OxiP`;f zl0QEvti|_st1-FC0Aq}?3hB=@AlmtBmET&Py#+v}A~q1Q8~fm#>)B)>08%I!Z+zJ$ zKoonU*OV_9|EU`-6S2}?vbwciZ~BHA8u=Uk#|AVMx0^AsJ-L=oeI5?Dl-He7yBpFt zYJra?T8mG*<{Sm@?9aw^UA{H#rwV8ek9db}hnuFrHfKIABlB?L(@>()6N8WzzTd{| z5{Xl8n$@W@v;%OV6fc$zzaC@P+4l)y+>829#8lJAtS)eQ`*5z;nH441>s@5KZ>%dfCJgn5iZ?1)4#-VpV|@s#OEFJwb5fzehaH1 z7(Wn)8FJExn>->D)ucH%N*u%FE3%T`1Q_58}GO##l;e}w3)Q{g+ zU5rpv73n64ZJIR64sL)`MR`HRrO#XH4Q+(~v&eJ+0TfSeUff+HpF`fE!-HD72Q3EJ z|C&siCT1qSXv!gE{9n*omLP#;XeIYKCCHE*L3xjxTNPf1fWqqbms8vguzPelz+e$! zXaXlWm6e77;62w0zLS6_;F!nj6E84!rUCm%0ecZ=Dm~+fPJ8|^0WlX6`~0x&BY*x~ z!E*s_0DsckU>SLHXG-d8%8FqFFq&E|P#F8?fSuU80oF}Z9M0ZeiC@;6auifm?@AYz z#b3}n9DHzAnHtBB(Zp?LCKtb>Q!Ag6(Nn!{zEC_Jvklk(CFa&ZsKQ}%$;||a_ zR?6C)M<)EB7W?*m51+m3nezW2Vl;3N^~F#c#2pfm|3Nkh!uhs50A!gnP=p;Ms87kwZDWbnZ$vB(_JaOl>69kj3iPD>0D8H4rwn4-WNgw-aZ#6V;j zfUf`pKM0|)=^M4vj?3f70(iiF>GWpk1u=tG0K;9Tb?@L9$b8<{B6EI#GZNq-x?Yvm z`4RP*1w$w8n|Evkna2h3$>^qjd&YkaZl?jdx^@yhG-sewsiOL0wckWrR#dFme)k(v z5j=v0P*(sZ0$8IzGDZplRscJQWLs&Y(bg~QZ%ikB)y5wS{Ly~e zO*Hie7g~3o`8sgLkT|nu^gNTK|MB@SR))4DJhBycR6y7ZA)b+A3#lrj5KK?LQ`4V_uiGai)P{ z>a7B4xp**$<1?BmoF-|y{Ba8|ZSjdnz5~y25yS(v^HSfEpxN>OHC!qPKo9?c;MJKU z!*0z0049u&M=egglJ{5Ea74e-SXmLWo8PJ`6hWbgq|zO)FlP0_Pw@RciB%m<5(u`O z5A2a8>a1JO&*%j|UG^?vCAI|qzKPZ$a>1Tw8>E;~=5Ee;`c*yNF;93k2ZD zVaV)C@K(i&mPE>+=V39gfdFmyU2i%d6I7dEwp*oy%_syifgQS?u{cBPRnc!R{awI1 z)`{3|B&G?$kt-K5@3H!TJSD*qwFElQF}`vNBn#yB>W;@hjBY z0(<1azZt0bt+!R*wcRiiB?=YU;5E!HNaE$h^{Q$<{ad}moTO7P`J?g}@Wd@2yN9g| zZNvDwZytDkC>zjfjX;q~_p#Y|%OLCsrWcx=JMf%~`V|sfL3OvGo9Z$8bEqk6LPBlc z`IhBsD&h)-Y3CG)Tpl65x%@WwH!2EA3cE39v)+)EV3z(kLefN#kEI~ph;Q&@r|o#y z#B&xYko+tg3g2LpBM`hQ*9%Bh&R5Yt`4y2u=Qiq>#0k0OR6EDx;WNA!WBAPvYZdV zQj;~T;blY;Z~icqR!-am^^e$d5By$^Bv{}HuQnvXLZ?AMmaZ?mlD7fgN}=V>P!r>J zX{ex1U;f@1%beGxHA6y+HB0->X}O0`Jb7t^&CR97n{cVHH4)Mn@KGuFJsDmKXKkw} zs11ja(bnh z0+D2Hq0egm&J6*KG-3{^F>MXuZ3`}#H$nc@y#PU9kl>lJ96y=7zY_pQqT(@@k;03@RC?p z7tGTuN4$dXc!<#ZbeKy$8^YGD_xHy#7E% zXMNFDZzV{XGAEBRlw7NE?M9P!b!1X}2-g3tf{2&G!>Kkqxm$6IblN)LXMf@Y!F}+I zm3PDh+R3TYEHIVeWK;AdsK8l2ZMSr|**65WFc%gHr4UKSPCKfMEmxzR>C8!tyC~nY zGRebYZy~&U-wx$SmdZVMWp;N6gL3)6=VPLO;n`pZJ#CUX(;nZbjss3AeqR4+J`A(q zVi;uxrNXp17W?UbN%yBQapCb^Pz+0iF!&n`sW4$11AVq#<3FY0@wmyLasX^AW5Y1_ zkTA%Y{r|3O%oe-=MS#1*^F!3Ktq-J&U|jby=W_$BcUU2Dgq}OfK7(;D)O{g}-5t;c#0l|N_(z@dGM_^C^O-y&wA-eh=@_-$)OvzC?v35fzg7`*J6PZxWE0J z-u_@Qnowe>xYi+zVy<7dy7g&01{x$|pWrsDd{>HErnVlgfBPNn6tPP&W3z#%KXLSe z0$1AI$4hZkVFI=DUEna)( zI^W(eyi38|y{x2;LQ5%z6B3utO5mG(M9gyK(DcVmDDdcJ@R16s@)~|_SGgGVf&LB? zH9XPL>NL@_XlLf@hUb8w5s;2IB$h$wi5ngLh0DcOF~g>-Ng^J^@R164uIxuyzA>vc zy3d4WH7zt7E5Mh&nAS&muXk~rQa4Ajhr!kaO5o2`wIaM&rLt_LHvQfE{?1!A02lai zf&ET%wf;#g_cw5X9ypE5lrKgJ(KB0JV*TZ_rH&%OQX&xTVrV(29wqL!zSU*y&EZ_kF3H&00$j!xW_ET{+^4^v>)$5$g8N@@y1vv_wd zHfM5rf$yMqPro&RhWT%(UXf?+I&iibvGh>{nNG*yblOZ3g-I* z*>hc7U*IOq_)S=x;tcZjhq^s@gbPK60V;D+Q0!oM@{7{%YMh0pa%`KaKLvQ6?NdPbJBizG3)OBo+FWdJBk-tNtz8uaqB=koOCj*sFUf zYo38gH!?ojuO)Nv&id*`!mkh}o3c5mlG1E6Mx6ump4z>Qs(SO;cS>9fq*H0MHJ_=i za1xNg$A8{o24kA%j%aM>iqc}y&bs&vv~?9{3Sp7Fbucx)rcBi&q(FSbU)MZy?*ZYL zr(5F~M-3xklQ-SsLH}IpZrRy?!8KCB&=D%PLFvTv*=JGu&=ocH=k@>|>*>2w?t~h& zfQy>XPHh#6<(6WTI>G?!2>wms%2;T}s(|{u!bBeDXnnPb@!%GJ<9uF@*I}dRNZiP; zMFi4Cz~Au8vrtBh4{#XkkxJkh?nZRj2ynJtWF&`V&R-O`?ew=*bZ9Emv<7vuk>QXY zmb`-POp>fSOs{p#^er!&;-h9l%Vzzia%L_DGeR#zV#KS?q!}FLR{!D`2Zkgc5Rkf` ziui@@Y(^^N9r(fbx;N~<^(OxRgdAIz20G7om}NH7%=1EXU&nW|*FFLY>Jtdzfqs#vOzWkQzK_Ibfiz`V^Ee<3^@6<7*xObe)v-pZ7a+a75y zpNCiDrjjxE)GL=+%s@PTbBclK_uGv=1)5mTvS$S3Y0D(v5h5M416%mB09$Bg{o%)(Efe)Js;_)$ zO{Fvx7#5;_6-^p>g9s>OM1Ste#2}M%3(ww=%HT314;L<7llpl9pB+?=%h>}gSg06? z2NlZTRadgS9^eX5#yJr_kfe@2Q&ihBm_vSc92;jd_oO6|be1=o4c-I_ag;49ECTi% zNh%C5`X`IB47b$FuLuEl)INxx<~^9SL*+qPsHh10(UJ=_f5V(>kXZyv6BOE8j)A>g zLSTE0eX>lU8pW5XnhzF3`CCx?itJ2Kn56-SpLJ|-EHf5^v$$`Rd7N5VV}&z`=#;q3 z2g(i+X-nc|xYsDQHffW!W0yt7Rz45SVPeHDz&Y?w zNqwE&yOy@3wHkP>lV+!|4Xzp2YdOzal9!Ktf)h*iM!cJ%3K`xE8Wv{R(v&JWcL#NtfUHPvQ`qKEBbi*bx~|0O@Q7qO+TR zc+GHR2wlH1w)pa71JK5BjRVzFJk zW652uQ0O#hYlUV9i1kZ{)r~^C@ps7+zHA9TRBVYs)9{EG9iUWD&%k|9%%HQk02n(v zakwg>M1w~*Kqf^AP@T6RLnaD&&@<<*e5oVipgo=#=ByJTj%%p3Zwws z5;qmLWFbB5@~S29AQVkqr?YTw4L&oW*eDQilf?3aOFCJ5rk9f~!M9N*Oe}X=mSe3MqpabfrXjK+xwh@w&n&@6j zg%R3|nNMlx1d>F3Rjn;+AfOSDR;rhWq8Sfq2of*g_@l7dpx+eVBkLzcHX-$G|CQyF zE-Uz|PBQ(JHS0{VACyFeuNug($%Q}U59rfaWQ zwJ1s8fyMV>FVxA|*Wik(+78cp#rU)r^n< zXEfg0;1Un+QvlGUIu~9Buhyv6X*lYf8ZI` z@h%6W6V(&*`EPl!vW_SZgpc81nHX1g-$56|ng1{Ju_a|{qd1{+a{XW>wz%CXyV{A6 zcM~QY$T*ve#2GmAcW)vV=DQs70mPAbA|Rl|RHn`lB?+vQD){!S2_L-=+E4_;S<=t< zv42+|%fuz2rj;$QA;_x}OX-)HDH*SpYb5^O6Nve@tVdH zm&OS&1|Ya)PGOMrJ3T9!9+5^99)$R_r%zN}CLj@vEXqtO4XecMP$+fP@6XUY{6g003?xgo7zGCw7tS%+QB0fskDsY~nU8n902 zj)f#jwdkvtmKy0MdkI1JPopF9S_PPcH6R>K`oZ`CZs6v33GW?Q33|xEHSizH`#~UJ zUV%)4;&0Xuzzd60J!G2)w_@M~uD87He46#1ulzLr91X-Dzihgt?ElrAhlvSN0k{AE z|NS{q_4M8B^eR@m1~p#R%ER02|Cl^Qo9=`5Mw>JMT$Kh;CZyuE(ml@sDBhp)fuTC6&ND@ae zf90X&C*>yxUZYTwwS%I!)LT#j9D7y_s^~Jqj#Tn2h_C=THVeifA_(MY#j}w^1e;Q` ze<7wG+_66TuY*QPGb!YslFUxapXT5OUjzEC+pZAF!7RGT0;{0TFdQc1-_iy%l@qmI zV2gM%Lo@{TYHP5b(4g&`Jp6XFKsQz?Gz#9zVu|6e^i7b3FqQ;z9WN}DMuGX-Nf7Vr7@eP+|xyaKuu&B3^|Mmm$a_axvZ zlDkWlBA$>Ss{UdIyIXxmq{HTqD;pSCatE8Mx~$cI|NsB}MgqP3&1KTh3ZIgCQh)#c ze3$A83&__`eYu!lYN!AIDoo-r!v_u|fPGM$x>dGPBkrbh!s%O95O=Q hbJ^GIDDUy)gN2KPtNg}G1ItEZfs6|F-~a#qoqYD!@V5W} literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-1.bin b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-1.bin new file mode 100644 index 0000000000000000000000000000000000000000..03c6a1528148b82ebab3e495f209edbfb33c957a GIT binary patch literal 2983 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�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 AssI20 literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.crc b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.crc new file mode 100644 index 00000000..dbad0a6a --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.crc @@ -0,0 +1,250 @@ +97f807e6 +ee7f1354 +6fa0cec6 +9285fe16 +b291ad06 +155fee7a +01427b08 +37610d09 +404fa256 +2935ba29 +fe05c389 +f4330069 +549eff98 +5150e3fb +10a15359 +0673d70d +6041a014 +104cba40 +47a31b16 +b5bc8a04 +3a16605a +da5384bc +156479bd +dc555188 +458bffed +2cc6fc99 +15abaa1b +bdddda11 +fffa939a +54a83ce1 +82ba8102 +0ac14f5a +49e4c2c8 +4ba7c6e5 +44f0bcb2 +c370a456 +6361c839 +5c0fdfc5 +dcd0b111 +adad6054 +67b38f70 +8decebb8 +d64dacb4 +4025f908 +d7154e44 +93fbddde +be5cf4ed +d4492e49 +44d25551 +fcfbdbcf +8698aa50 +00025a9a +45f6d961 +507fa2e0 +bda2b113 +d735c28e +13d54b15 +2b0e2c70 +fd1938c9 +e7b016c9 +511bbb25 +52754511 +f4c71342 +9572d1c1 +279aa775 +bee7e9aa +0e916552 +6bd17554 +0616896f +f2fbed3a +f2c2f64e +b34fea4e +fb9f27d0 +7bf93797 +58fe6b72 +d6dbb367 +a4351497 +6001755c +54e3ef07 +04bdff16 +f50bb46e +619d9976 +c7438682 +71186e2d +9f90810b +26fa0f46 +e276d45f +de8947eb +98c35c3e +5cd09c8d +6ffcf799 +d0af871b +b6f20a85 +b6064e3d +f8af1a1d +6d749e92 +484768ab +779f6743 +f98c027a +1579feb1 +8efcd176 +b7ab75fd +e7911455 +1878ec1f +db6c2d82 +8ad48e21 +9757e7ab +fd22cb3b +e441e63e +12bcd5e3 +f76a42e8 +261856b7 +ab00bc6f +03c25d9f +65ef13d7 +b66512c9 +415b3d05 +0dff93cb +cb541aaa +0ee6c61b +d6276a2c +900d44ad +0c408967 +2d539b08 +5479da37 +298f03dd +bac454b8 +81b81c33 +a99b8c77 +2cc2e8af +4053184a +24be8904 +5c46ee1c +30f2825f +311a5956 +8379cadc +16b91eea +9e765763 +c1115845 +8bf28db3 +40b89f88 +c785befb +b863d8a7 +d65c8552 +1571d9c1 +38c47640 +a04a8084 +d0881cc9 +e76534e9 +1b7b3ec0 +fa89a1e6 +91a77797 +50eabb2b +1ffea9fd +36da14d7 +4d21ead9 +0e9f3a19 +5c3bc82b +e408152c +aff45223 +4016bb65 +9055be73 +966f4f40 +1b232cde +19d409a7 +9cf5ada9 +b983a480 +4329c339 +1b8c3818 +0a7e1997 +cebc35c9 +a5483e3c +aa1ff50f +ad76567f +c1b9bd1a +8340988a +ecd60321 +8c12fdcf +e3744c63 +99c0fa4c +e1bdfb1f +a047f435 +7e3b8ebb +106e6518 +fb09be12 +d535a54d +23e91694 +1e68c0cb +53844c1a +c8180fa6 +a58b3a20 +bf8a3c82 +fbd86177 +27041f33 +88fe20d5 +c0dbae26 +1677f210 +1d40f687 +e37f402a +35d23b41 +8d0bd70e +1439b309 +bbe21347 +e03ae4c5 +13b745d7 +e5116c4e +70569ef4 +c85f4731 +b5b6de1f +9dc8c6ba +d7e7ec20 +fd5fdeb9 +1de6cf05 +968fe4ea +c658ebd9 +a3427991 +1d5aa7ef +6ae06a27 +41abbdb5 +a9104c0c +db2726a1 +d7f4ce5c +9ce1a83d +70252457 +6744fda6 +a5b475f8 +5718ff91 +d40f157d +50e7af4c +a49ec566 +1eda24b9 +b77eda17 +aac39c22 +2352e08d +a701b790 +cd916c2e +5ef244e6 +b9ee5202 +4a8b8eec +60a8976e +f98cbcb1 +d5a8094f +7b7c1585 +c679bf1d +e6bb3547 +4f05e0eb +d63111c5 +b8b260c0 +a18d9b72 +a7c1ca74 diff --git a/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.json b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.json new file mode 100644 index 00000000..145262a0 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.json @@ -0,0 +1,261 @@ +{ + "profile": "HEVCPROFILE_MAIN", + "width": 320, + "height": 240, + "frame_rate": 25, + "num_frames": 250, + "num_fragments": 254, + "md5_checksums": [ + "9d45302f59628259021c0d580f38e577", + "cfafb8cdd693af7b24292d98e28d6966", + "a9e93b65a59b752063f259c40e7d9fe7", + "8b2445ed2bde8128fe090ebf112299b3", + "af2fb4344c89078811eea7979ea87be8", + "031f754980442cb3d8c59e83f637f16b", + "3d579a05e18b85c5bf8e1079ebdb76d5", + "9414c26b10b707ce582bea5e067be2b4", + "18599153c70945dfde292dae24b81521", + "7a324a7822e3a94df8b5a767f42ae5b9", + "83ff81efb040a2d247abd639a66b76b7", + "0e5b73811f510ac2ed2b976b27d4a3d9", + "787e4f184a285d5c3071c14085ca528d", + "8f9d7714962d32e211ca88e3f1c7f46d", + "a40888217689af118a13d33ba1acee02", + "0dc38b3c25ef2e35a1a5d000f24066bd", + "f8bfaca952ef6158cac2b4139079ff3c", + "04dd4c10c44fe65111eec52cef42b79e", + "52e07c85f11add8e08473228c97d9e96", + "5d2d01028f9549b67452343e1a37159a", + "3e86b75a1c9cd4fc82371091dc9270ef", + "091c1d95044708b1699a704f7fe00e97", + "566eabd3b2112b1e60687c2646a03d7d", + "7febb218fcfcd622e78cadc575e00d74", + "c661462a644194e0e3f48cb76180f991", + "8a98f0f76e624e718ab7ee1f8b3d06d7", + "a74cee3ff950de4dfa7c049e72372d8a", + "2003bb8048e0778cd7b684d3d290e238", + "9583a569bef2a181e4b14ef9d7515965", + "28fc9c431f8895224ee76a7a6a069644", + "1f86380ff7e1ce3a549ec193f5442915", + "8ce13d9e3659b928c7b768c470863673", + "3e842fddf70acc3989b99a784d2d3a64", + "2161a6b08f354b9213673af0f10595a4", + "706ae034f4622cb729016b5efbf71761", + "9ab6c4ec2708fc446bd6e57c0943f9ce", + "77905702a6b7b1ad3a364914115ae1c1", + "f35817cdbb7fd60cd39363f8116474a5", + "ae41c4d453cf07939f652f79c6f2cc23", + "938bd9a612612c55a018f6a746eee14e", + "72b4d96412d20cdecce2861eacdf7e85", + "f392537536b52626f5bc338f8d5f6261", + "e7efe9c3e54ee42cfb1552c512844283", + "2560af598073488d3c49ef8c59d6010d", + "8a78183c164f50e206a7f376264103ab", + "3f4235a1536c838c0bdc273314ded61e", + "5887551fbb1436f5bc9fa6e63b9d9da0", + "77fb5a36e251287e8b93e8c12279f54f", + "36c1d5aef11aa43bff133907478abc07", + "7988ba44232599cc54acaa71bfcac8ee", + "a420156487134f7fd8b2bffc86c2c466", + "a27f4e7c47212d83dce57ba52e4bf728", + "651da9f2f35798255fa618affc9d5140", + "736f41a6fc11267e14d865a44a5024d5", + "5a0c9f520021a5e027287905443326c0", + "838c3ef02a0d419f79b0a5cb96100712", + "5d135fae4da8f33e842fe1d56b771baa", + "9f5b762f5d0a9e6b4373aca87c04f872", + "6a28827f8b5217636a003eba52bff2ab", + "117254f82ee37acee64c1695da6efa61", + "b9c023f5a72cbf120872794f94025c55", + "70ed7dfa85fa637e446a4aa941f2b9fe", + "7dcfc63d25fe08bc05c0c1b919148b57", + "830c4e8958e84338f7b0a2ae8ecac5b7", + "518e7456c889f30d1a4afdf2d0618f4b", + "8a5aa2e84a77756adf020b9b5570a47e", + "906878b088da07f0b5b61e2975aed09f", + "6f8a49de618fceee2c2284037ccb4181", + "f0f751f49e75b38373c2a5a4f2a8d9b8", + "a4ae82030d893596129f19c51158e595", + "35d0a14a2c4086c4ca4f010d2e9f0adb", + "9c39936e95c658ada88bb6e8885f4639", + "11bac53610bf107cfa9a80cc7d40d7ef", + "3b1377f3a2bbc03be2a04ccce3f26d02", + "520e6cb06673ece706cc96db877bd606", + "8ab7f21cf1de8ce902f15ce812bdbdbe", + "f140e1564aac1f71728922702083f9e9", + "51828833d2bcb377fa0be61b7cb82579", + "426f15aa7c01101d16237eb33995e4bf", + "8f00d6311bb2ce1d290433126e480114", + "1feaca010259e859bd3363b5c1d04f35", + "2bb0e4dd7b3a115bc0f445497e35dc08", + "fc640243683109158e0dff5dcea50842", + "867f0ecd82aae7740bd9b251b9179350", + "85111785a9ed8b2b8b70b09e12154428", + "40ab1b731a9bf8f8e441c70ccc19b1dd", + "3100c669e824c8aec2b2d67561db1349", + "d05552052cd675c02caf5e1c671be35b", + "47fc5cd75aca27be5d6dadde030fa3d5", + "da2cc8eafa37e9a057e9a569ee541637", + "d2b4a325071110060e847fd3e8ef1f52", + "1dcccb0cf3a74b3153cb9b7c09a7f377", + "8c91eb6aa2594899c0101d537505a121", + "13abb158d7c22362ef1b73b5f15bfecf", + "35ad01fb07169fc0050cdb996de53d9e", + "d25ce524d14d95fd99607d42e13b7937", + "c2ff502083b013873ab203d2af83337e", + "68567f7ce7b9b19e5f85872442c54f79", + "5ae94fee674131ecfd45a6775854ec96", + "1c9f092f1e02caca84086b9873e1af89", + "6510b165141b38e47d7cdc23e44cf39c", + "913b03bbb72f8ccba56440632377b342", + "3cf908dcf12bd787901571e601af20d7", + "25518a7a40234f05661aaefea13518b2", + "6d3512a84091b9a8934da296e8dfc5c4", + "8ca79a3ace49c1126687674fffaaa5a2", + "e644740d09c3e2f3598fce3839497dab", + "8342af1710d8fec8f10a64da2ca9ea01", + "7dc733dc534e2cca2422027db2a3e34a", + "50fd7033960cf1d08bb558f7f7b7743d", + "0cab05f4bc4f716df0c741a34a128f3f", + "ff12d8589ee2ba883462db3496625218", + "cfb4baf656a92ffad4f8e29e086a22ed", + "6276eaef79a25eeb77e15c26121b522e", + "5af976088fc10e9b263bf12d27af2131", + "27510f90e0599ab31df00771f712cfe7", + "5e120f23d16415907b3bca93c61a7798", + "f0822f6593be64c890a73b5e19fa0aba", + "6f00bf23a01f93f6cdea4c5ec9d429ba", + "acf06eafb385d75956ac11b4394d8a96", + "99f22f1087710da1e13fa51638b1cd1b", + "206d93afe06167faf8560300ae7d9e02", + "f0ac45f756a2ea398aa8c5cc6557048f", + "314169ee8e31d3d654e6b60b63ea6b94", + "b709914ad6a828552323fd7814aa3a69", + "16eedf463a872c4e4f9645e2e7f731c2", + "9ecaeec7815eda54aeb7ac937df6688e", + "d3f880557ed0e69595d2887a879c46bf", + "79afbc938ea2f2e0968b9cf31c7d653c", + "4e76d541aa8796cfe4409da6f2d2afbc", + "5a9b81dee85caeaeb1e5eccb8a5017cf", + "977e990539d1cf3088ffe511f7346953", + "2e0e69b8c37d91453b95cd3e4635ab4f", + "3768fcaea9f36b873b054d02e7386a44", + "600c3f478e859e5114fca6188e0832c3", + "7a02082f7f1f104c3f01be9707be6978", + "b5ba5c7504bdee1e10d74c3ebdd6d6b8", + "baecb5bc56ac7ff840164b35bdf555c4", + "33584ab96f9c3b6d7808e3e117b5b8a2", + "3cfbd17bcdec301728b833feb9f3c7fa", + "74daa5525ff24e90e6a1772a8e3055cf", + "a37c2933e7445a765c8f3f18f3384c93", + "e2b7199fcef9de93d4296cb3667bceae", + "adc2b4e150ece651e7bc1174a78f79c2", + "8568addcb675d4086158303460594703", + "81a2b4bf06a0bbcc851779890cd73756", + "c3f3f15b6ab9e47ce9242e32ff4951a6", + "0495b5f57f93e37a3cfe3eff9f52319e", + "36ffcdf358c7a42c427a6c827117a428", + "9bc6ade4706bcd242b6b9b4a50fab72c", + "584b7f4b14d531f8c8ac045a505da5d2", + "393180967f07275e834e21127cf40ff6", + "5dc3b033f2f26a4c0f113c60fe2d30bb", + "157e3c22f8f6a319bd77a7c7e9689d96", + "24395b2b073cb0d7448a7eec6ecb5edb", + "6fc1d9efc86af03f5939f3aa1a1f9dfd", + "ded02a863d25e29f9d1a78e8953cca16", + "275c63d4d60e0bcb63613bec7d3ebba3", + "042a6db4563ec75d5ff675a1e0e06647", + "872755f0743242d0b8fdc42ac5b81d4d", + "0aee140a64076aa43c8f9fb74d18796b", + "ba738328a5377253b7067843fe48cbe4", + "ba4fcdd5085f5bb3b33420c0eb97b5ab", + "c393ac063ba4ee7370595f1528b00e98", + "39ef16ba57122f6dd3bbec8aa83c596f", + "ce7baa5845683d115d17e2822238ea56", + "afd200eb4ed02c2ff93a8e04503490ac", + "df783cda0056926ae9687bcdff01d6cd", + "24129bf695474e4944cbf79c27dc49d5", + "bb62851ee3852094c3722b71c469764d", + "04a64b8e4f5a26bb03eeed2d6e63e7ad", + "54732dc13d72d9f57580ebf69943d927", + "b88ad31f39b05cf5f305be2ddb073f6f", + "9cd5b43a79eb5ee177c8bba8ac15d168", + "950909eefb9170dee1d45b3969e00427", + "23e208e28a0bde09a3f046aa167bed38", + "ed2706a02ba04c9b4a1c1b8cfffb04a3", + "588a2708e07d0b338a2ed07cbea6af80", + "23059a68238c316e7822233ed2f6eb7a", + "e7cebf9ac14981339822d58b36cd4167", + "3428cef2bda3072f21dcc48bb54d3042", + "2110db4e61e860db243e6ddfd48e84d0", + "0f2ea09489ff2b0b7202ece325580741", + "ab6aad6bcb8fb77126ee5a2ebb039d23", + "36882f3914929a863054d0a216dc570d", + "e6b9a67f2314c460d0e8dadc84e979d8", + "582bdd8b90609e15502a5c9f70ca9baa", + "9fa97542b318a264b496d49c73bb2d19", + "8da8a03cf4017c330226f4718ed449f7", + "b7f15e01dca2d411491444b724ffbd68", + "0c336bd2a32f5fb269803611d3e623a4", + "c1b129d76a277373c6748927436794a1", + "f303f6cb6a53b5998b3df6d273e502fb", + "99b90cdc84ff584deff8b85be4de29f2", + "3beb71970b6ad6d42ad08ac2b077bb23", + "f4952ee5cf74f1a1c65ca51c1dcac9b4", + "1636578dcfcfb9fc7dbabe14e3f85322", + "20242c41b958e6fcaf16ce3c5e5ac2df", + "1f5398fde49a13097929bb51ee4c6c5c", + "6b426035d763bbfd14b24ccea8e7bfd1", + "66123deb830f5d697ffdfb48e8310ed6", + "189ba318c114f516bb9c7598319facb6", + "a41c4331dd165fed91a4520efefa60c9", + "96e3507c43dd3966884ba5f841673dc4", + "a4308628349e5b94d6151d128d62f8f9", + "24b9cb3b410c8bff36af293af5f1f6c4", + "c5f17e29d80e18551fa11edc9c533e3d", + "ca89232bbc3b590a12c21e4392437a92", + "150542ffea542c14a0ec1ca46add7d83", + "f0074cb65b559bc01f92ee0b2b2d851c", + "c20e6eaa9005a856a1295c5c43b849b5", + "61c579376b7d08d44982cd58efd2f2f3", + "cd7c18087349de540dc8c7ee53bca62e", + "545dda07c02e9ef635d10da90129712e", + "af193247eb37e7e2b237bec00a68fea5", + "a1a1368be1b97d9eb671dc59d2529e72", + "e51153a36891bc8c22edae72954af0af", + "82a2a2e229ee457b2a50e747fc05db45", + "6988aa461cb3dc59fb5b04aaff4af962", + "7617af042cf22ef700c27e3893878474", + "d84e5115883391ae8b4136de46eeb056", + "0674289ea495ed353988d4a2a39b1622", + "bbf870e8ff3e047fe87b37895ccb30a8", + "c62e8b15d60c0f45522c9607dcaef52c", + "6a2535d22f6c34f09868be21ada6063d", + "b54d58f9772149b9e40ba765a6f0c7a0", + "7a2bb00940c3ea825a5994fc6e1a5862", + "daccd8eb96035f7427c2ac984ef2669b", + "e93b78e2adbf25f11a13e4476cb5eb56", + "4621a7e39895cc326486e84f4000438a", + "f6cfda926b4214d72983d1abb8d7f0bf", + "15222df4f32a096b2178364382bb02b5", + "5825ed69a82645fda8a6e86b4c48aeaa", + "690fbef93df5dbc0316eff2a04e30244", + "0eeee51628e3a3d6943266a0a95bf4ff", + "7204f8130ec97d750287c03a613bdf23", + "fade1a84465a00a62c91325ecfdb788e", + "e061571ce0382db61922852144786a27", + "e369e19a152688b1dcc910784d41739d", + "bdc6190b3e945bea34219fc4a1849926", + "c0dd752a1b1143e6b41d411fd423ae17", + "08fe1c3af42005990a61685a2f35383c", + "029de50a465d0f816070c3d5f17bde37", + "bc75689a45409bffc566ff7f099030c2", + "2e577e6c6a15e33a7b3f26b01ab1497a", + "08038a3f13e60ba8bdde60a09ae429a3", + "1f6e4ac664308335b10cb31da28f03c3", + "e7c9e3c8ff869ffba99c64385dd58db5", + "855c9cc4f404f747d2af60dff6b3db3a", + "b6ae874fc675a3fd0f69cf36515cf8e5" + ] +} + diff --git a/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.md5 b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.md5 new file mode 100644 index 00000000..b460f218 --- /dev/null +++ b/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.md5 @@ -0,0 +1,250 @@ +3aa1404b3c67f08effee1b22a1edfe35 +126904a814874104c62aaf430100013e +57a22d9cefafaf079f065bfb4c30153a +5e03e8591340813fe3f84b0f02a31570 +6ead9483dd1cb7e15c325525081aec55 +379af5d20d2745f7ef89ee902651d159 +d3ad92e815413ac0df5a152dff1513ef +a5a2774087b82062477b966c956ee507 +6372aea236df163e92abf32a23955586 +99f10be97458407433c6bc7e76e453c0 +699aa7d9609dde914f7a315da43b85a4 +b29a0c1424632d002dd6fc65a85ae702 +fbb0bbe60d3574300c4324350dd9cf94 +abd49b7181e7533e32d582cb89f4a890 +ece092ba9c23de4fd597d1d590b2fd5c +71fbd2135bcb03651fa3fce07fe212c2 +ab2f4e90aa360da6a67eb0b4b94d89a5 +c3ebfa439f56a5d4a1274642ccf5fe88 +e3646565b25d19be356d725c9ccdf571 +10ad345a9199940edc4cf259fd740d67 +b25f027b25711e949ca188e9b7c071b7 +79f38a78fa14b251c3d13b25829be484 +2f471f7f9e841bf8d0a71fec8c99edab +4386d538763d73d5f7ca8727aa0a1d0d +be88a7beb8bfd95c5eb03493f0a3d5ad +0beaeb365979f8355db831d8a39805c2 +c854fb790d5189b93bae7f5a8ee3c634 +557d551050575b0d7c36c103e587ed51 +bf1c245bf54695757f00d9a04defe688 +9f21b22a7e887e382b83a5fc070212e1 +97f63b86fb8dcdd3eb4e80be92672475 +9f6df014df10bd5983c15aebc5d9b0ae +9c8437e809cf388325101c45720d2965 +caf27527cf3cdef68449a8515bc271ab +1d2ffaed9291656e526ba9f33cc6edba +85a827b0a35ed63bf1ccd33aa8fa2fbb +c8d67d86ea8b4ff56591288f3b354881 +a690a754d9a51ae87a000f41d6157324 +cb31f37e307feeaeff6aa0df28ec4a63 +0ae07bc4b807aa17d831312ac7f8be00 +4a73a078bab877b40f4230d905b2c657 +4f99a4b8b320f540ac2ce76ee08a5131 +0e8043071109eaa1f983fa9f89c9893d +9827495323c5df10b900bed10caac240 +cf1e190b0cbe8a395630d9dbb8926f3e +aa8511fa4ddfcfb9c68d7cafb8dea11c +a49a2fc1c52f716d1fdb4eb75a148658 +5f25b4f699445d8af438b77a58554eb6 +734344be019030baf3801a2c238a3b81 +9d82b03748e1f39e795ed2e7c8048233 +26656158f2dc3e44b8ecfc302b6ba50d +2bdc0637fb6f1834bca11ed7a884d3d5 +a4c41fb0b47337bec3f98732bc2d72d6 +21b8fec70f82c45281e86651f3c10b4a +4bd700ca4c31f0b67c85a11d57013310 +d3c6545314d53d68ba842d5f65784230 +2f1826430cdc477c82bbf48c7178d4d0 +11cb5dcc7932047eff832efca8dabff9 +0d00bd23075c075240df89baf1533931 +7b2e8f4c9ce1edd3d88613bd38e1347a +224d1d502dfe17e65280afdb0944a768 +55f5e305c52979a4280310e91eb99c3e +7b021942409264048613a0bac9f261fa +98962ca6ee07c708b1cebf93d92ec706 +5901a19c4a240037bb1b1efe7ee0afb5 +6f49b4efe4155cf2e209c17d9e4bed3a +5fb93030e6f55756004b5abf3e5a9f47 +b6f1473717ef4ef16cff8721a51f53ce +e66e2f9382528cbbf4041dc1b315e8b3 +5104df3dd59e8f8c029ceb1da0dbe170 +aac0cd3df566a3b7d5a72bc5202c7752 +b97493d2bd5da6a25462f09dbe86a8a6 +c9dc6f8e721de915864d6a39be93171d +c67a196f47a9d8aca36d33dabd1c60c6 +f98dca89ac1d9da1e4ecd2f00748f8ca +4b223546c3f83f24c50ef023bdcfe096 +4a8d486fb79d01706cd51a2c7d446afb +a25a13b07a66c230edd3ac6aa97f2d70 +a478b43a9b2f7cb8086056ff981f3d82 +3a86ca51898cb11bfbf47f32c4e1948e +d48f1ec0f0740d506553472b5038001c +1e0597118f13b08c78fcb8d3e445e89d +0d3a037c2ea82a7adf739987eb77a4b3 +70cf1159356ad666fbff7eb33a4e220b +49aca942deb5340e899ec93a70c467bb +a4d8a4fe1963990af2167ddd193d2a2e +7da7b5197dcb138788d66742913d7238 +a806177db926b0c4b469b52329c32bc2 +3b0da86ca6b69266628b474ba4c6dbf5 +7547a5b2c8b5d4613e81ee3b6d7003f2 +80afa451394825bf9f61b000bd001857 +40807c933935dd92abb61e2b2da1fcc8 +508fdefd102f9e1f7804d39eae916655 +91d9ee56f07f46d1ec191e662bd935f8 +807c81583b0073a2746d752a4b1f42df +30734e3d8a710b269f5a29ce589b8a57 +4cdde1ffffc3dabe5b7e8f5c0ff8f084 +be6c0266091a2f3363dae1df8f724c9c +8858b0f16fb5babda61c4a6beb0523ca +16ffb13b8ff3aec78e8066cef06913f6 +3aed790a2749291489b12520da443dad +c57ba43539016f901db55073386d5cd9 +0f8f41751bd1fdc06b72c4d82d350d84 +aef1772f3fdcef057f6924a40da7c2d8 +f5f8b1460650b41f06cf8391bc18e426 +a729195174db4ceea1973eb6165a9d6e +e5022f7d2feaf57faa1e38ff1fc5bf68 +c694bf08869619a1bcc289127a408ed0 +0caaeabd4abbcc6d3bbca0b8b5d7cdb1 +971bdb6165a17e75773adeff5cf65fba +5bcba13d46fd61c73341d2d362962872 +95bc9f0f82248551d9b41d799beb08ac +7495973421c51dd51958d54b542ecc29 +7205d43bd4aeb3675aafb0b257d43136 +0720c6b6c9936f8457aeb9172c030a53 +6560c536d9c02d726f2f1d3737087ece +83dbde13fa583e0e6444dd6efb011b34 +23080ac9e24b9698a2abf602ccfd2d09 +ff9c9e733f7e8ba1d74fcc40b7f89762 +65a541bfb561ec6cd945283e00175039 +4c57d75ca3f96e852f24f11b5b9944f8 +1e42dc0503db109faa0ce0a4c2d1eb5b +284fc104d59d56c359631879d4ca5ae4 +a433eeff0d24300f106528ff3ee97dd9 +8b3cc99a2ba8b51ab37ff8e43ec39d88 +c883537d57780448f19b1fa22b731ddc +34ebc05648788fa25e14aecc00c941b0 +2a522bbb0f652b825f4546bd483b002a +1cd101eecab9fa491786310326aa02aa +c34b70539d2cf79ba8c4814ca0b2b2f4 +6a445c83d65f30a519d6f1257de3a5d8 +7d006c2ced504c486d09fbb5c70d8f48 +1ecaaf8b8b79c0134d224408feb4b12e +4f6fc08223fa794700c7fe2e13552aef +89c50b145e15de68a635acdc56118fdf +cf7d298515c15f7b969998211ed76d5c +22007d9a686417c64151ce79d890f1f6 +091caca7499775a91fba09b45d0efeba +6141cbf235f3d5f9ee7921795ac12948 +b5f9f13c06aaba11aba62edd8680a619 +cb81aeb1845c13741d7306e05ec620b5 +9e38b9005b5f87093069b0733186f92a +2cb31fe3452d1e1b188ac8071bbd2145 +38e1dc763e76422f652b4791ab0d00a0 +491418db6354101d9e9221f9a366ef4e +9dd1f141bac7936ce04b31d0d0e80db7 +f53ea317e8e16c5c556454b5139c6a3f +e4b632d8bd400354cf8d03bfac61987f +61195150e317268a4d8809db46344c01 +ff66c261ab7e00745d5f6dbd696dc4de +5e090a50613045d8f71460a9de77547e +83b8182ba7a6b77117eeb729f9d6fd53 +941a4dfa7c33d3a84993505f406e58aa +5b2dab7f75283b9d05a1baeb465afb8f +ccccd7a54b1b162b47267ad3c633588b +b649cc2512d02ded5f8fb1a5e6c4e468 +36d2229b2a85ca5eb5e627d4fefe5d0e +58f856aabbd3bb8df15d226c2c86f53a +6218b94752b4936b2a9332ad78ed3c78 +46317cf4bccec620087f52c7da5af936 +6f1d6769eafa76baa638f5eacd68bea8 +240996e0719b183d04808ed92b36ec02 +24cb70c1de6c09acb298836f591ccf60 +62abeb55d923017366c3c0b5913b290c +2074b8a3aaed5d41493578617834f865 +9bab5c6da1b475040a1ef3ad96039f16 +61a3914b0a1c2b563cc6be02f7c52c39 +9214566cdf649774366168904ba5a631 +b29a21da4b59070f8938175cf982610e +712b8106393fa454db36d409829fd446 +7303c34c4ee40273c96d97aa193254d1 +34aac7ac642a02409cea8ddc0e79ca5a +c905bb704eb57a5803b5f628a1885e65 +cfaeb64926d2cc463d2b039e25796e2c +631aa39682688ca2a9fa1b5ac046c84b +35e029947bb6ca6436f2287e56194910 +9559137575c4bdf3e4e32794c30d566e +b26eb2e52cd85d5c62d02ceef8beca5e +eda954629e3db49ce785db968dfd7435 +0c4db062f0a361a085e02a82c62ae623 +25505fb97d5a024c6a8c756b94417c85 +7145b53631fcdaf67d46f578dc061709 +86196a74f056323957abe0db608e063b +4a2cec3448951953c76676511ce24068 +34b6f3e81afd9002411772b291d376f9 +0a889a8d77a4f7d001734e5590a1143d +b2dd18c9a76ff8df04202b70a08e8480 +4966084a71252407273f57638148c878 +a9a0c3cd10496210ad5f423c67fd388c +c7cf572ff8d7d859e255a1865012af47 +2d3fad5c6403435842002f02012c23ed +e45d5b0fa1f296600bd8f6bdb91d59ff +b308537943d50f9cf6afb9289826f21f +1a6eacecb41a9984e8324900b7eafe43 +cee6bc8991e350ff29b2002d47e473c6 +fcb9de16c63bb0504dc97c3936f01479 +20ba7f47ea85ec2c1ede90415f69f96c +edfc01d0213d0da38817b95c7f2090fd +e89dd0d6f9982683375fd43fc10f8d33 +3937797142661006c3287488b3218b12 +33f13996becd2161301afdc6397c0582 +d8df3b5bbfd905c12c244c0252e49760 +fbd6b8d0fe1da3e6fe5464720212b94f +b48262d193c09a00ba217cf056832696 +ca7bb0ae9e2baca1669dee5b4181cba3 +556adb32715d449d18032f2b1b0d5bb8 +2cda45ece79f91c91d97dbecbfdb77ff +02be10a3e8fcef9ddaf31ec127117346 +a1bd20b8e168dca0743acee02e266bb8 +37959f49652a6a63c78700852ec32f99 +612cbca363753f225e4236e4ef7cbf08 +3593229c535f68e9285c13b91d2a84d0 +76dfdfdcae9abe98b65b0183f56d81b0 +b01c94b49b38b5f1b3cefcb0bb5baf4e +aa41e02d44472fc5bf4b7d70eedf6317 +1f08d61e6780d63803337b2ee8120ac3 +d96c5b0c7616a32308a3873a93055940 +fbedfad1f84c85373ed740fa7d9faee9 +8900bc3aea596ba8957fc57edd2590d5 +660524d84aa663b32198423942525e2a +ce749f243fdaa7ddbf79ef3ee145f8bb +bb22ba1c54cb0c1535d64a1b947ffcff +fe1af0925d781c8e733d0bbb93234d6a +6d29a1bdb45482834fc37adbfa4de834 +77f09e7535a1745cfde19b7e262b4a6f +cb6d2d2d5e94ab426166ccc46b8df0ad +a49a9e733db88096180a27411c66d211 +3d7caaddb5f71b238fb2c253ab835396 +7feab55c011e16389d52ee203187f55b +c06bc6b54bad2330d11c7ad711c8ee9a +49569cc2cd80e956e0bd0ee178abee42 +392eadde235ec3f44c16e347040e7dc3 +d358513ffb159bbe5d93f3cebf24f4e7 +b2b6c35f846328ffe100c2d6bcff2091 +868971765ea4221a6f8854847c65b2b3 +3b0d0a5b426029b3ccc7eba61e4a8fdb +0230e17adf575c13decdd0d225fed9a8 +24ccb608f3f4ee193f9553709ac112c0 +8da1c9cddc689c17b6a40df060769c6f +52c6bd707266df417b6ca83e83befe49 +95fea0bccde2991e6fe7476444517c3b +bd0cd099fa62bc0db33593e28c2b140c +04c8032d5f34150d4f011c6322fdc5c6 +71735c0de711d3e34f053927e25c1e26 +98853c3c86949059572bd5d711b91b26 +cc9a35cb0bde5916476212203f7af380 +5346c2a8f3fee6ff9a3068b52bcf590f +b13980ab3982fb964f80606acdf0dfb4 +d3137b6453aa267bc67be303db77630e +e71431bb7d535ddad95ed24bcea40451 diff --git a/vendor/cros-codecs/src/codec/vp8.rs b/vendor/cros-codecs/src/codec/vp8.rs new file mode 100644 index 00000000..c8db8ff6 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8.rs @@ -0,0 +1,11 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#![warn(clippy::missing_panics_doc)] +#![warn(clippy::panic)] +#![warn(clippy::unwrap_used)] + +mod bool_decoder; +pub mod parser; +mod probs; diff --git a/vendor/cros-codecs/src/codec/vp8/bool_decoder.rs b/vendor/cros-codecs/src/codec/vp8/bool_decoder.rs new file mode 100644 index 00000000..eebe2a5d --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/bool_decoder.rs @@ -0,0 +1,309 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! A VP8 boolean decoder based on the implementation in Chromium and GStreamer. + +use std::convert::TryFrom; +use std::fmt; + +use crate::bitstream_utils::BitReader; + +const LOTS_OF_BITS: u32 = 0x40000000; +const U8_BITS: usize = u8::BITS as usize; +const BD_VALUE_SIZE: usize = std::mem::size_of::() * U8_BITS; + +const NORM: [u8; 256] = [ + 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 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, 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, 1, 1, 1, 1, 1, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +/// Some bits are "encoded" with a 50/50 probability. +const DEFAULT_PROBABILITY: u8 = 128; + +/// A capture of the state of the boolean decoder. +/// +/// A `BoolDecoder` can be consumed and turned into this structure, which captures its state. This +/// is useful to pass that state to the next decoding step, typically a hardware accelerator. +pub struct BoolDecoderState { + pub range: usize, + pub value: usize, + pub count: isize, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum BoolDecoderError { + EndOfInput, + CannotConvert, +} + +impl fmt::Display for BoolDecoderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + BoolDecoderError::EndOfInput => write!(f, "end of input reached"), + BoolDecoderError::CannotConvert => { + write!(f, "could not convert number of read bits to target type") + } + } + } +} + +pub type BoolDecoderResult = std::result::Result; + +/// The decoder state. +pub struct BoolDecoder<'a> { + data: BitReader<'a>, + range: usize, + value: usize, + count: isize, +} + +impl<'a> BoolDecoder<'a> { + /// Creates a new instance. + pub fn new(data: &'a [u8]) -> Self { + Self { + data: BitReader::new(data, false), + range: 255usize, + value: 0usize, + count: -(U8_BITS as isize), + } + } + + /// Fills more bits from `data` to `value`. We shall keep at least 8 bits of the current `data` + /// in `value`. + /// + /// Returns `Some(())` if there was input data to fill from, `None` if we reached the end of + /// the input. + fn fill(&mut self) -> Option<()> { + let mut shift = + (BD_VALUE_SIZE as isize - U8_BITS as isize - (self.count + U8_BITS as isize)) as i32; + let bits_left = self.data.num_bits_left() as i32; + let x = shift + U8_BITS as i32 - bits_left; + let mut loop_end = 0; + + if x >= 0 { + self.count += LOTS_OF_BITS as isize; + loop_end = x; + } + + if x < 0 || bits_left != 0 { + while shift >= loop_end { + self.count += U8_BITS as isize; + self.value |= self.data.read_bits::(8).ok()? << shift; + shift -= U8_BITS as i32; + } + Some(()) + } else { + None + } + } + + /// Reads the next bit from the coded stream. The probability of the bit to + /// be one is probability / 256. + fn read_bit(&mut self, probability: u8) -> BoolDecoderResult { + let split = 1 + (((self.range - 1) * probability as usize) >> 8); + + if self.count < 0 { + self.fill().ok_or(BoolDecoderError::EndOfInput)?; + } + + let bigsplit = split << (BD_VALUE_SIZE - U8_BITS); + + let bit = if self.value >= bigsplit { + self.range -= split; + self.value -= bigsplit; + true + } else { + self.range = split; + false + }; + + let shift = NORM[self.range]; + self.range <<= shift; + self.value <<= shift; + self.count -= isize::from(shift); + + Ok(bit) + } + + /// Reads a "literal", that is, a "num_bits"-wide unsigned value whose bits + /// come high- to low-order, with each bit encoded at probability 1/2. + /// + /// # Panics + /// + /// Will panic if `nbits > 31`. + fn read_literal(&mut self, nbits: usize) -> BoolDecoderResult { + // This won't perform well if we read more than 31 bits. + assert!(nbits <= 31); + + let mut ret = 0; + + for _ in 0..nbits { + ret = (ret << 1) | self.read_bit(DEFAULT_PROBABILITY)? as i32; + } + + Ok(ret) + } + + /// Reads a boolean from the coded stream. Returns false if it has reached the + /// end of data and failed to read the boolean. The probability of out to + /// be true is probability / 256, e.g., when probability is 0x80, the + /// chance is 1/2 (i.e., 0x80 / 256). + pub fn read_bool(&mut self) -> BoolDecoderResult { + self.read_literal(1).map(|bit| bit != 0) + } + + /// Reads a boolean from the coded stream. Returns false if it has reached the + /// end of data and failed to read the boolean. The probability of out to + /// be true is probability / 256, e.g., when probability is 0x80, the + /// chance is 1/2 (i.e., 0x80 / 256). + pub fn read_bool_with_prob(&mut self, probability: u8) -> BoolDecoderResult { + self.read_bit(probability) + } + + /// Reads an unsigned literal from the coded stream. + /// + /// # Panics + /// + /// Will panic if `nbits > 31`. + pub fn read_uint>(&mut self, nbits: usize) -> BoolDecoderResult { + let value = self.read_literal(nbits)?; + U::try_from(value).map_err(|_| BoolDecoderError::CannotConvert) + } + + /// Reads a literal with sign from the coded stream. This is similar to the + /// read_literal(), it first read a "num_bits"-wide unsigned value, and then + /// read an extra bit as the sign of the literal. + /// + /// # Panics + /// + /// Will panic if `nbits > 31`. + pub fn read_sint>(&mut self, nbits: usize) -> BoolDecoderResult { + let mut value = self.read_literal(nbits)?; + let sign = self.read_bool()?; + + if sign { + value = -value; + } + + U::try_from(value).map_err(|_| BoolDecoderError::CannotConvert) + } + + /// Returns the current bit position. + pub fn pos(&self) -> usize { + let mut bit_count = (self.count + 8) as usize; + + if bit_count > BD_VALUE_SIZE { + bit_count = bit_count.saturating_sub(LOTS_OF_BITS as usize) + } + + let pos = self.data.position() as usize; + pos - bit_count + } +} + +impl From> for BoolDecoderState { + fn from(mut bd: BoolDecoder) -> Self { + if bd.count < 0 { + let _ = bd.fill(); + } + + Self { + value: bd.value >> (BD_VALUE_SIZE - U8_BITS), + count: (U8_BITS as isize + bd.count) % U8_BITS as isize, + range: bd.range, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const NUM_BITS_TO_TEST: usize = 100; + + /// 100 zeros with probability of 0x80. + const DATA_ZEROS_AND_EVEN_PROBABILITIES: [u8; 14] = + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + + /// 100 ones with probability of 0x80. + const DATA_ONES_AND_EVEN_PROBABILITIES: [u8; 14] = + [0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x20]; + + /// [0, 1, 0, 1, ..., 1] with probability [0, 1, 2, 3, ..., 99]. + const DATA_PARITIES_AND_INCREASING_PROBABILITIES: [u8; 21] = [ + 0x00, 0x02, 0x08, 0x31, 0x8e, 0xca, 0xab, 0xe2, 0xc8, 0x31, 0x12, 0xb3, 0x2c, 0x19, 0x90, + 0xc6, 0x6a, 0xeb, 0x17, 0x52, 0x30, + ]; + + // All tests adapted from: + // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/media/parsers/vp8_bool_decoder_unittest.cc + + #[test] + fn decode_bools_with_zeros_and_even_probabilities() { + let mut bd = BoolDecoder::new(&DATA_ZEROS_AND_EVEN_PROBABILITIES[..]); + assert!(bd.pos() == 0); + + for i in 0..NUM_BITS_TO_TEST { + assert_eq!(bd.read_bool_with_prob(0x80), Ok(false)); + assert_eq!(i, bd.pos()); + } + } + + #[test] + fn decode_literals_with_zeros_and_even_probabilities() { + // Adapted from: + // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/media/parsers/vp8_bool_decoder_unittest.cc + let mut bd = BoolDecoder::new(&DATA_ZEROS_AND_EVEN_PROBABILITIES[..]); + assert_eq!(bd.pos(), 0); + + assert_eq!(bd.read_literal(1), Ok(0)); + assert_eq!(bd.read_literal(31), Ok(0)); + assert_eq!(bd.read_sint::(1), Ok(0)); + assert_eq!(bd.read_sint::(31), Ok(0)); + } + + #[test] + fn decode_bools_with_ones_and_even_probabilities() { + let mut bd = BoolDecoder::new(&DATA_ONES_AND_EVEN_PROBABILITIES[..]); + assert!(bd.pos() == 0); + + for i in 0..NUM_BITS_TO_TEST { + assert_eq!(bd.read_bool_with_prob(0x80), Ok(true)); + assert_eq!(i + 1, bd.pos()); + } + } + + #[test] + fn decode_literals_with_ones_and_even_probabilities() { + let mut bd = BoolDecoder::new(&DATA_ONES_AND_EVEN_PROBABILITIES[..]); + assert_eq!(bd.pos(), 0); + + assert_eq!(bd.read_literal(1), Ok(1)); + assert_eq!(bd.read_literal(31), Ok(0x7fffffff)); + assert_eq!(bd.read_sint::(1), Ok(-1)); + assert_eq!(bd.read_sint::(31), Ok(-0x7fffffff)); + } + + #[test] + fn decode_bools_with_parities_and_increasing_probabilities() { + let mut bd = BoolDecoder::new(&DATA_PARITIES_AND_INCREASING_PROBABILITIES[..]); + assert!(bd.pos() == 0); + + for i in 0..NUM_BITS_TO_TEST { + let bit = bd.read_bool_with_prob(i as u8).unwrap(); + + if i % 2 == 0 { + assert!(!bit); + } else { + assert!(bit); + } + } + } +} diff --git a/vendor/cros-codecs/src/codec/vp8/parser.rs b/vendor/cros-codecs/src/codec/vp8/parser.rs new file mode 100644 index 00000000..04266d80 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/parser.rs @@ -0,0 +1,811 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::convert::TryFrom; +use std::fmt; + +use log::debug; + +use crate::bitstream_utils::BitReader; +use crate::codec::vp8::bool_decoder::BoolDecoder; +use crate::codec::vp8::bool_decoder::BoolDecoderError; +use crate::codec::vp8::bool_decoder::BoolDecoderResult; +use crate::codec::vp8::bool_decoder::BoolDecoderState; +use crate::codec::vp8::probs::COEFF_DEFAULT_PROBS; +use crate::codec::vp8::probs::COEFF_UPDATE_PROBS; +use crate::codec::vp8::probs::KF_UV_MODE_PROBS; +use crate::codec::vp8::probs::KF_Y_MODE_PROBS; +use crate::codec::vp8::probs::MV_DEFAULT_PROBS; +use crate::codec::vp8::probs::MV_UPDATE_PROBS; +use crate::codec::vp8::probs::NK_UV_MODE_PROBS; +use crate::codec::vp8::probs::NK_Y_MODE_PROBS; + +/// Dequantization indices as parsed from the quant_indices() syntax. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct QuantIndices { + /// The dequantization table index used for the luma AC coefficients (and + /// other coefficient groups if no delta value is present). + pub y_ac_qi: u8, + /// Indicates the delta value that is added to the baseline index to obtain + /// the luma DC coefficient dequantization index. + pub y_dc_delta: i8, + /// Indicates the delta value that is added to the baseline index to obtain + /// the Y2 block DC coefficient dequantization index. + pub y2_dc_delta: i8, + /// Indicates the delta value that is added to the baseline index to obtain + /// the Y2 block AC coefficient dequantization index. + pub y2_ac_delta: i8, + /// Indicates the delta value that is added to the baseline index to obtain + /// the chroma DC coefficient dequantization index. + pub uv_dc_delta: i8, + /// Indicates the delta value that is added to the baseline index to obtain + /// the chroma AC coefficient dequantization index. + pub uv_ac_delta: i8, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct MbLfAdjustments { + /// Indicates if the MB-level loop filter adjustment (based on the used + /// reference frame and coding mode) is on for the current frame. + pub loop_filter_adj_enable: bool, + /// Indicates if the delta values used in adjustment are updated in the + /// current frame. + pub mode_ref_lf_delta_update: bool, + + //if mode_ref_lf_delta_update == 1 + /// Indicates the adjustment delta value corresponding to a certain used + /// reference frame. + pub ref_frame_delta: [i8; 4], + /// Indicates the adjustment delta value corresponding to a certain MB + /// prediction mode + pub mb_mode_delta: [i8; 4], +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Segmentation { + /// Enables the segmentation feature for the current frame. + pub segmentation_enabled: bool, + /// Determines if the MB segmentation map is updated in the current frame. + pub update_mb_segmentation_map: bool, + /// indicates if the segment feature data is updated in the current frame. + pub update_segment_feature_data: bool, + + // If update_segment_feature_data == 1 + /// Indicates the feature data update mode, O for delta and 1 for the + /// absolute value. + pub segment_feature_mode: bool, + /// Indicates if the quantizer value is updated for the izh segment. + pub quantizer_update_value: [i8; 4], + /// Indicates the update value for the loop filter level. + pub lf_update_value: [i8; 4], + + // if update_mb_segmentation_map == 1 + /// The branch probabilities of the segment id decoding tree. + pub segment_prob: [u8; 3], +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ModeProbs { + /// Branch probabilities of the luma intra prediction mode decoding tree, + /// kept live between frames. + pub intra_16x16_prob: [u8; 4], + /// Branch probabilities of the chroma intra prediction mode decoding tree, + /// kept live between frames. + pub intra_chroma_prob: [u8; 3], +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Header { + /// Indicates if the current frame is a key frame or not. + pub key_frame: bool, + /// Determines the bitstream version. + pub version: u8, + /// Indicates if the current frame is meant to be displayed or not. + pub show_frame: bool, + /// The size in bytes of the Uncompressed Data Chunk + pub data_chunk_size: u8, + /// Determines the size of the first partition (control partition) excluding + /// the size of the Uncompressed Data Chunk + pub first_part_size: u32, + + /// The frame's width, in pixels. + pub width: u16, + /// The frame's height, in pixels. + pub height: u16, + /// Horizontal scale code value. + pub horiz_scale_code: u8, + /// Vertical scale code value. + pub vert_scale_code: u8, + /// Defines the YUV color space of the sequence. + pub color_space: bool, + /// Specifies if the decoder is required to clamp the reconstructed pixel + /// values. + pub clamping_type: bool, + /// Determines whether the normal or the simple loop filter is used. + pub filter_type: bool, + /// Controls the deblocking filter. + pub loop_filter_level: u8, + /// Controls the deblocking filter. + pub sharpness_level: u8, + /// Determines the number of separate partitions containing the DCT + /// coefficients of the macroblocks. + log2_nbr_of_dct_partitions: u8, + + pub partition_size: [u32; 8], + + /// Dequantizer indices. + pub quant_indices: QuantIndices, + + /// Determines whether updated token probabilities are used only for this + /// frame or until further update + pub refresh_entropy_probs: bool, + /// Determines if the current decoded frame refreshes the last frame + /// reference buffer + pub refresh_last: bool, + + /// Determines if the current decoded frame refreshes the golden frame. + pub refresh_golden_frame: bool, + /// Determines if the current decoded frame refreshes the alternate + /// reference frame. + pub refresh_alternate_frame: bool, + /// Determines if the golden reference is replaced by another reference. + pub copy_buffer_to_golden: u8, + /// Determines if the alternate reference is replaced by another reference. + pub copy_buffer_to_alternate: u8, + /// Controls the sign of motion vectors when the golden frame is referenced. + pub sign_bias_golden: bool, + /// Controls the sign of motion vectors when the alternate frame is + /// referenced. + pub sign_bias_alternate: bool, + + /// The new branch probability for the DCT/WHT tree. + pub coeff_prob: [[[[u8; 11]; 3]; 8]; 4], + /// MV decoding probability. + pub mv_prob: [[u8; 19]; 2], + + /// Enables or disables the skipping of macroblocks containing no non-zero + /// coefficients. + pub mb_no_coeff_skip: bool, + /// The probability that the macroblock is not skipped (flag indicating + /// skipped macroblock is false). + pub prob_skip_false: u8, + /// The probability of an intra macroblock. + pub prob_intra: u8, + /// The probability that the last reference frame is used for inter + /// prediction. + pub prob_last: u8, + /// The probability that the golden reference frame is used for inter + /// prediction. + pub prob_golden: u8, + /// Branch probabilities kept live across frames. + pub mode_probs: ModeProbs, + + /// Boolean decoder `range` for this frame. + pub bd_range: usize, + /// Boolean decoder `value` for this frame. + pub bd_value: usize, + /// Boolean decoder `count` for this frame. + pub bd_count: isize, + + /// The size in bits of the Frame Header, thus excluding any Uncompressed + /// Data Chunk bytes. + pub header_size: u32, +} + +#[derive(Debug)] +pub enum ParseUncompressedChunkError { + InvalidStartCode(u32), + IoError(String), +} + +impl fmt::Display for ParseUncompressedChunkError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParseUncompressedChunkError::InvalidStartCode(x) => { + write!(f, "invalid start code {}", x) + } + ParseUncompressedChunkError::IoError(x) => write!(f, "I/O error: {}", x), + } + } +} + +#[derive(Debug)] +pub enum ComputePartitionSizesError { + EndOfHeader, + PartitionTooLarge, +} + +impl fmt::Display for ComputePartitionSizesError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ComputePartitionSizesError::EndOfHeader => write!(f, "unexpected end of header"), + ComputePartitionSizesError::PartitionTooLarge => { + write!(f, "partition size not fitting in a u32") + } + } + } +} + +impl Header { + /// Returns the number of separate partitions containing the DCT coefficients of the + /// macroblocks. + pub fn num_dct_partitions(&self) -> usize { + 1 << self.log2_nbr_of_dct_partitions + } + + /// Returns the total size of the encoded frame in bytes, as computed from the header. + pub fn frame_len(&self) -> usize { + // Uncompressed chunk size. + std::iter::once(self.data_chunk_size as usize) + // Size of first partition. + .chain(std::iter::once(self.first_part_size as usize)) + // Size of the partitions description area. + .chain(std::iter::once(self.num_dct_partitions().saturating_sub(1) * 3)) + // Size of other DCT partitions. + .chain(self.partition_size.iter().take(self.num_dct_partitions()).map(|s| *s as usize)) + .sum() + } + + /// Create a new `Header` by parsing the uncompressed data chunk of a frame. + fn parse_uncompressed_data_chunk( + bitstream: &[u8], + ) -> Result { + debug!("Parsing VP8 uncompressed data chunk."); + + let mut reader = BitReader::new(bitstream, false); + + let frame_tag = + reader.read_le::(3).map_err(|err| ParseUncompressedChunkError::IoError(err))?; + + let mut header = Header { + key_frame: (frame_tag & 0x1) == 0, + version: ((frame_tag >> 1) & 0x07) as u8, + show_frame: ((frame_tag >> 4) & 0x1) != 0, + first_part_size: (frame_tag >> 5) & 0x7ffff, + ..Default::default() + }; + + if header.key_frame { + let start_code = reader + .read_le::(3) + .map_err(|err| ParseUncompressedChunkError::IoError(err))?; + + if start_code != 0x2a019d { + return Err(ParseUncompressedChunkError::InvalidStartCode(start_code)); + } + + let size_code = reader + .read_le::(2) + .map_err(|err| ParseUncompressedChunkError::IoError(err))?; + header.horiz_scale_code = (size_code >> 14) as u8; + header.width = size_code & 0x3fff; + + let size_code = reader + .read_le::(2) + .map_err(|err| ParseUncompressedChunkError::IoError(err))?; + header.vert_scale_code = (size_code >> 14) as u8; + header.height = size_code & 0x3fff; + } + + if reader.position() % 8 != 0 { + Err(ParseUncompressedChunkError::IoError("Misaligned VP8 header".into())) + } else { + header.data_chunk_size = (reader.position() / 8) as u8; + Ok(header) + } + } + + fn compute_partition_sizes(&mut self, data: &[u8]) -> Result<(), ComputePartitionSizesError> { + let num_partitions = self.num_dct_partitions(); + let mut part_size_ofs = self.first_part_size as usize; + let mut ofs = part_size_ofs + 3 * (num_partitions - 1); + + if ofs > data.len() { + return Err(ComputePartitionSizesError::EndOfHeader); + } + + for i in 0..num_partitions - 1 { + let b0 = u32::from(data[part_size_ofs]); + let b1 = u32::from(data[part_size_ofs + 1]) << 8; + let b2 = u32::from(data[part_size_ofs + 2]) << 16; + + let part_size = b0 | b1 | b2; + part_size_ofs += 3; + + self.partition_size[i] = part_size; + ofs += part_size as usize; + } + + if ofs > data.len() { + return Err(ComputePartitionSizesError::EndOfHeader); + } + + self.partition_size[num_partitions - 1] = u32::try_from(data.len() - ofs) + .map_err(|_| ComputePartitionSizesError::PartitionTooLarge)?; + Ok(()) + } +} + +/// A VP8 frame. +pub struct Frame<'a> { + /// The bitstream data for this frame. + bitstream: &'a [u8], + /// The actual length of the frame data within `bitstream`. + frame_len: usize, + /// The parsed frame header. + pub header: Header, +} + +impl<'a> AsRef<[u8]> for Frame<'a> { + fn as_ref(&self) -> &[u8] { + &self.bitstream[..self.frame_len] + } +} + +/// A VP8 parser based on GStreamer's vp8parser and Chromium's VP8 parser. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Parser { + /// Segmentation data kept live across frames. + segmentation: Segmentation, + /// MbLfAdjustments data kept live across frames. + mb_lf_adjust: MbLfAdjustments, + /// Coeff probabilities data kept live across frames. + coeff_prob: [[[[u8; 11]; 3]; 8]; 4], + /// Motion vector probabilities data kept live across frames. + mv_prob: [[u8; 19]; 2], + /// Branch probabilities kept live across frames. + mode_probs: ModeProbs, +} + +#[derive(Debug)] +pub enum ParseFrameError { + ParseUncompressedChunk(ParseUncompressedChunkError), + InvalidPartitionSize(usize, usize), + ParseFrameHeader(BoolDecoderError), + ComputePartitionSizes(ComputePartitionSizesError), + BitstreamTooShort(usize, usize), +} + +impl fmt::Display for ParseFrameError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParseFrameError::ParseUncompressedChunk(x) => { + write!(f, "error while parsing uncompressed chunk of frame: {}", x) + } + ParseFrameError::InvalidPartitionSize(x, y) => { + write!(f, "partition end {} is bigger than bitstream length {}", x, y) + } + ParseFrameError::ParseFrameHeader(x) => { + write!(f, "error while parsing frame header: {}", x) + } + ParseFrameError::ComputePartitionSizes(x) => { + write!(f, "error while computing frames partitions sizes: {}", x) + } + ParseFrameError::BitstreamTooShort(x, y) => { + write!(f, "bitstream is shorter ({} bytes) than computed length of frame {}", x, y) + } + } + } +} + +impl From for ParseFrameError { + fn from(err: ParseUncompressedChunkError) -> Self { + ParseFrameError::ParseUncompressedChunk(err) + } +} + +impl From for ParseFrameError { + fn from(err: BoolDecoderError) -> Self { + ParseFrameError::ParseFrameHeader(err) + } +} + +impl From for ParseFrameError { + fn from(err: ComputePartitionSizesError) -> Self { + ParseFrameError::ComputePartitionSizes(err) + } +} + +impl Parser { + pub fn segmentation(&self) -> &Segmentation { + &self.segmentation + } + + pub fn mb_lf_adjust(&self) -> &MbLfAdjustments { + &self.mb_lf_adjust + } + + fn mode_probs_init_defaults(mode_probs: &mut ModeProbs, key_frame: bool) { + if key_frame { + mode_probs.intra_16x16_prob = KF_Y_MODE_PROBS; + mode_probs.intra_chroma_prob = KF_UV_MODE_PROBS; + } else { + mode_probs.intra_16x16_prob = NK_Y_MODE_PROBS; + mode_probs.intra_chroma_prob = NK_UV_MODE_PROBS; + } + } + + fn update_segmentation(bd: &mut BoolDecoder, seg: &mut Segmentation) -> BoolDecoderResult<()> { + seg.update_mb_segmentation_map = false; + seg.update_segment_feature_data = false; + + seg.segmentation_enabled = bd.read_bool()?; + if !seg.segmentation_enabled { + return Ok(()); + } + + seg.update_mb_segmentation_map = bd.read_bool()?; + seg.update_segment_feature_data = bd.read_bool()?; + + if seg.update_segment_feature_data { + seg.segment_feature_mode = bd.read_bool()?; + + for value in seg.quantizer_update_value.iter_mut() { + let update = bd.read_bool()?; + if update { + *value = bd.read_sint(7)?; + } else { + // quantizer_update_value defaults to zero if update flag is + // zero (Section 9.3, 4.b) + *value = 0; + } + } + + for value in seg.lf_update_value.iter_mut() { + let update = bd.read_bool()?; + if update { + *value = bd.read_sint(6)?; + } else { + // lf_update_value defaults to zero if update flag is + // zero (Section 9.3, 4.b) + *value = 0; + } + } + + if seg.update_mb_segmentation_map { + for value in seg.segment_prob.iter_mut() { + let update = bd.read_bool()?; + if update { + *value = bd.read_uint(8)?; + } else { + // segment_prob defaults to 255 if update flag is + // zero (Section 9.3, 5) + *value = 255; + } + } + } + } + + Ok(()) + } + + fn parse_mb_lf_adjustments( + bd: &mut BoolDecoder, + adj: &mut MbLfAdjustments, + ) -> BoolDecoderResult<()> { + adj.mode_ref_lf_delta_update = false; + + adj.loop_filter_adj_enable = bd.read_bool()?; + if !adj.loop_filter_adj_enable { + return Ok(()); + } + + adj.mode_ref_lf_delta_update = bd.read_bool()?; + if !adj.mode_ref_lf_delta_update { + return Ok(()); + } + + for value in adj.ref_frame_delta.iter_mut() { + let update = bd.read_bool()?; + if update { + *value = bd.read_sint(6)?; + } + } + + for value in adj.mb_mode_delta.iter_mut() { + let update = bd.read_bool()?; + if update { + *value = bd.read_sint(6)?; + } + } + + Ok(()) + } + + fn parse_quant_indices(bd: &mut BoolDecoder, q: &mut QuantIndices) -> BoolDecoderResult<()> { + q.y_ac_qi = bd.read_uint(7)?; + + let y_dc_delta_present = bd.read_bool()?; + + if y_dc_delta_present { + q.y_dc_delta = bd.read_sint(4)?; + } else { + q.y_dc_delta = 0; + } + + let y2_dc_delta_present = bd.read_bool()?; + if y2_dc_delta_present { + q.y2_dc_delta = bd.read_sint(4)?; + } else { + q.y2_dc_delta = 0; + } + + let y2_ac_delta_present = bd.read_bool()?; + if y2_ac_delta_present { + q.y2_ac_delta = bd.read_sint(4)?; + } else { + q.y2_ac_delta = 0; + } + + let uv_dc_delta_present = bd.read_bool()?; + if uv_dc_delta_present { + q.uv_dc_delta = bd.read_sint(4)?; + } else { + q.uv_dc_delta = 0; + } + + let uv_ac_delta_present = bd.read_bool()?; + if uv_ac_delta_present { + q.uv_ac_delta = bd.read_sint(4)?; + } else { + q.uv_ac_delta = 0; + } + + Ok(()) + } + + fn parse_token_prob_update( + bd: &mut BoolDecoder, + coeff_probs: &mut [[[[u8; 11]; 3]; 8]; 4], + ) -> BoolDecoderResult<()> { + for (i, vi) in coeff_probs.iter_mut().enumerate() { + for (j, vj) in vi.iter_mut().enumerate() { + for (k, vk) in vj.iter_mut().enumerate() { + for (l, prob) in vk.iter_mut().enumerate() { + let update = bd.read_bool_with_prob(COEFF_UPDATE_PROBS[i][j][k][l])?; + if update { + *prob = bd.read_uint(8)?; + } + } + } + } + } + + Ok(()) + } + + fn parse_mv_prob_update( + bd: &mut BoolDecoder, + mv_probs: &mut [[u8; 19]; 2], + ) -> BoolDecoderResult<()> { + for (i, vi) in mv_probs.iter_mut().enumerate() { + for (j, prob) in vi.iter_mut().enumerate() { + let update = bd.read_bool_with_prob(MV_UPDATE_PROBS[i][j])?; + if update { + let mv_prob_update = bd.read_uint::(7)?; + + if mv_prob_update > 0 { + *prob = mv_prob_update << 1; + } else { + *prob = 1; + } + } + } + } + + Ok(()) + } + + fn parse_frame_header(&mut self, data: &[u8], frame: &mut Header) -> BoolDecoderResult<()> { + debug!("Parsing VP8 frame header."); + let mut bd = BoolDecoder::new(data); + + if frame.key_frame { + frame.color_space = bd.read_bool()?; + frame.clamping_type = bd.read_bool()?; + } + + Parser::update_segmentation(&mut bd, &mut self.segmentation)?; + + frame.filter_type = bd.read_bool()?; + frame.loop_filter_level = bd.read_uint(6)?; + frame.sharpness_level = bd.read_uint(3)?; + + Parser::parse_mb_lf_adjustments(&mut bd, &mut self.mb_lf_adjust)?; + + frame.log2_nbr_of_dct_partitions = bd.read_uint(2)?; + + Parser::parse_quant_indices(&mut bd, &mut frame.quant_indices)?; + + frame.copy_buffer_to_golden = 0; + frame.copy_buffer_to_alternate = 0; + + if frame.key_frame { + frame.refresh_entropy_probs = bd.read_bool()?; + + frame.refresh_last = true; + frame.refresh_golden_frame = true; + frame.refresh_alternate_frame = true; + + Parser::mode_probs_init_defaults(&mut frame.mode_probs, true); + } else { + frame.refresh_golden_frame = bd.read_bool()?; + frame.refresh_alternate_frame = bd.read_bool()?; + + if !frame.refresh_golden_frame { + frame.copy_buffer_to_golden = bd.read_uint(2)?; + } + + if !frame.refresh_alternate_frame { + frame.copy_buffer_to_alternate = bd.read_uint(2)?; + } + + frame.sign_bias_golden = bd.read_bool()?; + frame.sign_bias_alternate = bd.read_bool()?; + frame.refresh_entropy_probs = bd.read_bool()?; + frame.refresh_last = bd.read_bool()?; + + frame.mode_probs = self.mode_probs.clone(); + } + + frame.coeff_prob = self.coeff_prob; + frame.mv_prob = self.mv_prob; + + Parser::parse_token_prob_update(&mut bd, &mut frame.coeff_prob)?; + + frame.mb_no_coeff_skip = bd.read_bool()?; + if frame.mb_no_coeff_skip { + frame.prob_skip_false = bd.read_uint(8)?; + } + + if !frame.key_frame { + frame.prob_intra = bd.read_uint(8)?; + frame.prob_last = bd.read_uint(8)?; + frame.prob_golden = bd.read_uint(8)?; + + let intra_16x16_prob_update_flag = bd.read_bool()?; + if intra_16x16_prob_update_flag { + for prob in frame.mode_probs.intra_16x16_prob.iter_mut() { + *prob = bd.read_uint(8)?; + } + } + + let intra_chroma_prob_update_flag = bd.read_bool()?; + if intra_chroma_prob_update_flag { + for prob in frame.mode_probs.intra_chroma_prob.iter_mut() { + *prob = bd.read_uint(8)?; + } + } + + Parser::parse_mv_prob_update(&mut bd, &mut frame.mv_prob)?; + } + + if frame.refresh_entropy_probs { + self.coeff_prob = frame.coeff_prob; + self.mv_prob = frame.mv_prob; + + if !frame.key_frame { + self.mode_probs = frame.mode_probs.clone(); + } + } + + frame.header_size = bd.pos() as u32; + + let state: BoolDecoderState = bd.into(); + frame.bd_range = state.range; + frame.bd_value = state.value; + frame.bd_count = state.count; + + Ok(()) + } + + /// Parse a single frame from the chunk in `data`. + pub fn parse_frame<'a>(&mut self, bitstream: &'a [u8]) -> Result, ParseFrameError> { + let mut header = Header::parse_uncompressed_data_chunk(bitstream)?; + if header.key_frame { + // Reset on every key frame. + *self = Default::default(); + } + + let first_part_end = header.data_chunk_size as usize + header.first_part_size as usize; + + if first_part_end > bitstream.len() { + return Err(ParseFrameError::InvalidPartitionSize(first_part_end, bitstream.len())); + } + + let compressed_area = &bitstream[header.data_chunk_size as usize..]; + + self.parse_frame_header(compressed_area, &mut header)?; + header.compute_partition_sizes(compressed_area)?; + + let frame_len = header.frame_len(); + if frame_len > bitstream.as_ref().len() { + return Err(ParseFrameError::BitstreamTooShort(bitstream.as_ref().len(), frame_len)); + } + + Ok(Frame { bitstream, frame_len, header }) + } +} + +impl Default for Parser { + fn default() -> Self { + Self { + segmentation: Default::default(), + mb_lf_adjust: Default::default(), + coeff_prob: COEFF_DEFAULT_PROBS, + mv_prob: MV_DEFAULT_PROBS, + mode_probs: ModeProbs { + intra_16x16_prob: NK_Y_MODE_PROBS, + intra_chroma_prob: NK_UV_MODE_PROBS, + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::Parser; + + // Test and test data extracted from GStreamer + // subprojects/gst-plugins-bad/tests/check/libs/vp8parser.c + const VP8_TEST_0_INTRA: &[u8] = include_bytes!("test_data/vp8-parser-test-0-intra.bin"); + const VP8_TEST_0_INTER: &[u8] = include_bytes!("test_data/vp8-parser-test-0-inter.bin"); + + #[test] + fn gst_intra() { + let mut parser = Parser::default(); + let frame = parser.parse_frame(VP8_TEST_0_INTRA).expect("Parsing a intra frame failed"); + + assert!(frame.header.key_frame); + + assert_eq!(frame.header.first_part_size, 234); + assert_eq!(frame.header.width, 176); + assert_eq!(frame.header.height, 144); + + assert!(parser.mb_lf_adjust.loop_filter_adj_enable); + assert!(parser.mb_lf_adjust.mode_ref_lf_delta_update); + + assert_eq!(parser.mb_lf_adjust.ref_frame_delta[0], 2); + assert_eq!(parser.mb_lf_adjust.ref_frame_delta[1], 0); + assert_eq!(parser.mb_lf_adjust.ref_frame_delta[2], -2); + assert_eq!(parser.mb_lf_adjust.ref_frame_delta[3], -2); + + assert_eq!(parser.mb_lf_adjust.mb_mode_delta[0], 4); + assert_eq!(parser.mb_lf_adjust.mb_mode_delta[1], -2); + assert_eq!(parser.mb_lf_adjust.mb_mode_delta[2], 2); + assert_eq!(parser.mb_lf_adjust.mb_mode_delta[3], 4); + + assert_eq!(frame.header.quant_indices.y_ac_qi, 4); + assert!(frame.header.mb_no_coeff_skip); + + assert_eq!(frame.header.bd_range, 0xe8); + assert_eq!(frame.header.bd_value, 0x68); + assert_eq!(frame.header.bd_count, 1); + } + + #[test] + fn gst_inter() { + let mut parser = Parser::default(); + let frame = parser.parse_frame(VP8_TEST_0_INTER).expect("Parsing a inter frame failed"); + + assert!(!frame.header.key_frame); + + assert_eq!(frame.header.first_part_size, 98); + assert!(parser.mb_lf_adjust.loop_filter_adj_enable); + assert_eq!(frame.header.quant_indices.y_ac_qi, 4); + + assert!(frame.header.refresh_entropy_probs); + assert!(frame.header.refresh_last); + assert!(frame.header.mb_no_coeff_skip); + + assert_eq!(frame.header.prob_skip_false, 131); + assert_eq!(frame.header.prob_intra, 224); + assert_eq!(frame.header.prob_last, 233); + assert_eq!(frame.header.prob_golden, 1); + + assert_eq!(frame.header.bd_range, 0x8e); + assert_eq!(frame.header.bd_value, 0x85); + assert_eq!(frame.header.bd_count, 5); + } +} diff --git a/vendor/cros-codecs/src/codec/vp8/probs.rs b/vendor/cros-codecs/src/codec/vp8/probs.rs new file mode 100644 index 00000000..3bcc2cf0 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/probs.rs @@ -0,0 +1,365 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! VP8 probability tables as per the reference software and specification. + +pub const MV_UPDATE_PROBS: [[u8; 19]; 2] = [ + [237, 246, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 250, 250, 252, 254, 254], + [231, 243, 245, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 251, 251, 254, 254, 254], +]; + +pub const MV_DEFAULT_PROBS: [[u8; 19]; 2] = [ + [162, 128, 225, 146, 172, 147, 214, 39, 156, 128, 129, 132, 75, 145, 178, 206, 239, 254, 254], + [164, 128, 204, 170, 119, 235, 140, 230, 228, 128, 130, 130, 74, 148, 180, 203, 236, 254, 254], +]; + +pub const NK_Y_MODE_PROBS: [u8; 4] = [112, 86, 140, 37]; + +pub const KF_Y_MODE_PROBS: [u8; 4] = [145, 156, 163, 128]; + +pub const NK_UV_MODE_PROBS: [u8; 3] = [162, 101, 204]; + +pub const KF_UV_MODE_PROBS: [u8; 3] = [142, 114, 183]; + +pub const COEFF_UPDATE_PROBS: [[[[u8; 11]; 3]; 8]; 4] = [ + [ + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255], + [249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255], + [234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255], + [250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255], + [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + ], + [ + [ + [217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255], + [234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255], + ], + [ + [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255], + [250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + ], + [ + [ + [186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255], + [234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255], + [251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255], + ], + [ + [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + ], + [ + [ + [248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255], + [248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255], + [246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255], + [252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255], + [248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255], + [253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255], + [252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255], + [250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + ], + ], +]; + +pub const COEFF_DEFAULT_PROBS: [[[[u8; 11]; 3]; 8]; 4] = [ + [ + [ + [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128], + [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128], + [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128], + ], + [ + [253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128], + [189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128], + [106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128], + ], + [ + [1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128], + [181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128], + [78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128], + ], + [ + [1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128], + [184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128], + [77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128], + ], + [ + [1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128], + [170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128], + [37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128], + ], + [ + [1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128], + [207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128], + [102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128], + ], + [ + [1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128], + [177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128], + [80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128], + ], + [ + [1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128], + [246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128], + [255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128], + ], + ], + [ + [ + [198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62], + [131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1], + [68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128], + ], + [ + [1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128], + [184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128], + [81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128], + ], + [ + [1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128], + [99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128], + [23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128], + ], + [ + [1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128], + [109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128], + [44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128], + ], + [ + [1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128], + [94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128], + [22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128], + ], + [ + [1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128], + [124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128], + [35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128], + ], + [ + [1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128], + [121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128], + [45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128], + ], + [ + [1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128], + [203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128], + [137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128], + ], + ], + [ + [ + [253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128], + [175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128], + [73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128], + ], + [ + [1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128], + [239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128], + [155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128], + ], + [ + [1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128], + [201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128], + [69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128], + ], + [ + [1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128], + [223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128], + [141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128], + ], + [ + [1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128], + [190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128], + [149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128], + ], + [ + [1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128], + [247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128], + [240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128], + ], + [ + [1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128], + [213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128], + [55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128], + ], + [ + [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128], + [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128], + [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128], + ], + ], + [ + [ + [202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255], + [126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128], + [61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128], + ], + [ + [1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128], + [166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128], + [39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128], + ], + [ + [1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128], + [124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128], + [24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128], + ], + [ + [1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128], + [149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128], + [28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128], + ], + [ + [1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128], + [123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128], + [20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128], + ], + [ + [1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128], + [168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128], + [47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128], + ], + [ + [1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128], + [141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128], + [42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128], + ], + [ + [1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128], + [244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128], + [238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128], + ], + ], +]; diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/README.md b/vendor/cros-codecs/src/codec/vp8/test_data/README.md new file mode 100644 index 00000000..a8e0fa6b --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/README.md @@ -0,0 +1,10 @@ +# VP8 Test Data + +This document lists the test data used by the VP8 decoder. + +Unless otherwise noted, the CRCs were computed using GStreamer's VA-API decoder in +`gst-plugins-bad`. + +## test-25fps.vp8 + +Same as Chromium's `test-25fps.vp8`. diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/gen_crcs.sh b/vendor/cros-codecs/src/codec/vp8/test_data/gen_crcs.sh new file mode 100755 index 00000000..812facb2 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/gen_crcs.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Generates the CRCs for all .vp8 files in the current directory using ffmpeg. + +for f in `ls *.vp8`; do + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5 +done diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-0.bin b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-0.bin new file mode 100644 index 00000000..46ea618c --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-0.bin @@ -0,0 +1,2 @@ +ۀۀj~I3bN ƴIۀMn怀e%tfg`ӫ˒8က.F쀀f%#>-ݬܝD/Еݢ߀ǀQccyʀ[m,^ھdǀ뀀|ꀀ#M̀y-cU ^+3I =_ԷOZM`̀%3ĺE.䀀/|n$[7]]&_(8<:F4pKǿEQmbD +21:$4A.BrAJY$ݨG9>0ssգXHF(52Fuz*Q\#>R2P$$8;bt,怀Gz39'=Qyg6T}{.f/>T>{E:L \ No newline at end of file diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-1.bin b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-1.bin new file mode 100644 index 00000000..19b40626 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-1.bin @@ -0,0 +1,2 @@ +ۀۀj~I3bN ƴIۀMn怀e%tfg`ӫ˒8က.F쀀f%#>-ݬܝD/Еݢ߀ǀQccyʀ[m,^ھdǀ뀀|ꀀ#M̀y-cU ^+3I =_ԷOZM`̀%3ĺE.䀀/|n$[7]]&_(8<:F4pKǿEQmbD +21:$4A.BrAJY$ݨG9>0ssգXHF(52Fuz*Q\#>R2P$$8;bt,怀Gz39'=QygT}{.f/>T>{E:L \ No newline at end of file diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-2.bin b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-2.bin new file mode 100644 index 00000000..19b40626 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps-vp8-probability-table-2.bin @@ -0,0 +1,2 @@ +ۀۀj~I3bN ƴIۀMn怀e%tfg`ӫ˒8က.F쀀f%#>-ݬܝD/Еݢ߀ǀQccyʀ[m,^ھdǀ뀀|ꀀ#M̀y-cU ^+3I =_ԷOZM`̀%3ĺE.䀀/|n$[7]]&_(8<:F4pKǿEQmbD +21:$4A.BrAJY$ݨG9>0ssգXHF(52Fuz*Q\#>R2P$$8;bt,怀Gz39'=QygT}{.f/>T>{E:L \ No newline at end of file diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8 b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8 new file mode 100644 index 0000000000000000000000000000000000000000..dc22d4379221f2926d1bde3eeec112abd12a31f1 GIT binary patch literal 255719 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

    xJDuYUL9Wz#h{$-sfh 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

    g^(wJTrK71D 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 z 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

    nt<$pT^h=XMYvLysmu$H+ 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�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

    Ng|vv*RY z9~c+H{=Opl#2!j{wu%5i1REug7A$a9j7uGn>B_wKw!yUfk*0%G*q%d_!rN+^?0ETV z-zE;579%tyjlD0nf}prm3RD!;^9FN`kgc8IbiYe$R#mD1576#RCD&uBuV=yWtmWRo z_zEEZ8@>Ozg#AB{dmxIEzi&Y)A6SV?xsWD?xFr@{ox#^ zysZHb?>%*ue|-x7!-Fj%3~$fa#I%8JJsf|44#mgs1` z0f=Q4XmAK*N6S&dN8V1){oGo?_HpPW+p4E&^j%x@FR*V0OR0!Dfr z&BfVP0-d5aH0*W!2^wO|Qi+uW+f5{V2xM|u+QV;Nd@O|ZP2cTzGn(v4REURQzYoX0 z=e8znZAF8|0jhOYgG-8$p45L+w0LkPRa;s5tZ%6WHsC4^7Y!=OB`em*%>~AD z#LzC6E(&ao_&c#0S)$d?qK6>#>PHqSj5x4G-hGq1$pXp-wBYvFXm*GY9W&R7K#V(9 zlvpwj(Eyj_HA=~4aI{}>$K}Z*yvMC?7m%}aSNaAu8 zd~H4a0_*z(YmDty4xV4hB!48{7UGicD6>$?XRY%iUGLZ5BsG!Rl{g627qdY^DHk4z zQ%J?uc0El1lt?DMV_zb1xCkH2c`)Z!Q330wb@AM$y;a4GZas#|s$K$A#c}Lv(;PM) z)4bE^lLOM3SNhIN*)p?(nquT8?i5gZ#ic?Vt6!hDsQ!Rz-rkIlm#rJRtjJo=`83OdqFEfHmsF#-hI}r}z?kVE zgza0_HjxAKtsc(X(#=GJAh#KY5t6<6^gPpO|ATPEA(S{p1?UbugsguLJz5o{!aAp6rQZvM6A0) zX`yeM2FLB1+I#ZTje%j%E{IBv#Uiw6+>SzA zgAWzn?T$?&|EAU>Ds#uDFO)P^hq+_TuBh~Q1MbND&3S4dE2H3@MJkM>)GDw^8gS-K z-W16h8I&J4wg2^gRc^JZc!t%YiG~0x(IQ3ONNbj`lLuB<+SV_%52!8Y>kV9vc{NV< zee|F5#JaK%@>bg}fP%bw#!PxMKD#QYSX?x=m)cQu!g%y_IT2y@cy)^cj_tf_+(NQ( z960E$9iCCooUHC-R7Gure`89 zv_Al06bB3BLzvp98m^rk1xd7D%eyNdfi~Fdg!WHB`cn30ZNwh!f zcPI{)y3_d-<5T<~uJDjIZQ4DZm_OoFT0&(G*w^ORH6@VR4Ue^V?D?#C6g@+Dm(9p0x$ti2 zi*3QCXMIQ8l&8dsYJ*2fe9{o5J!#qfxUP4n4q+5k%wKQYbZVloTmQg%j9GG*V*jpX z`pb@bmUR+=I8!rjgIp~K$}9}OEO*!;S+yL6R@sc%O;(Qs=7>)Oe&Q&#nyvLf|Ex+* zOOiD-h~LL&i6d5Kru@@smX3}+9qz{mzH&IEt@0jLdJ*C;W})1M3n^nysWVf;ufh6r|FziETJ^~kgPD3 zXdnOdjFf{^`M}@%0W}HIwcz2xMS$R7h){_A4TMqOoZj5;_U#f@apJObvP|G-tZa#S1_u;?}a$v!ld)F)0cgo!!%7OJtSh*FXZZV8RyoO;De7Rm9IY9eZuPkLTNhY zu_g!yM%aNPF?XJ7-6zRiM^8%6t~N_OefToF{@u;aVvE<3b~?yaPe%by?d_}&WP-pw zewQ8&n46Hu7Y#$00}Z*E%1AE>EoT?w!^BNh$@7Jq6j~&?PFhvyepK@$I7PCWDg~_( zo~qo{sID15rRu1Y+jH3n=mvhfFVkkn#=Ius-^$>*-=3>;dJ3g>w0h8V16CS(0qwZ=4>1 z^XO-c-Y|&zZG5HF_*N?Is59v^?TPJ)6}jog(}o_TSfh_I-wH&jK1nB_6K8q3l4*{!~97;3{*gW_H*2 z*`KQQ>vj{UEqP&1KScVXqR_V@qzpULfq(+S?-S!Bzrf>Nc0Dt6Mk%<|yhbx*U6y!@ z(DpBCMQ5}1IFVneob#WneBJr<>LAJ@D@wsMuyzq!R5j_y7SWw8bYCQt%Mdf*p8_R( z7izazMDrRTaT_RO$ygFkT=$Kq1U=hg_^pCyH78DOF~-Rs+gHPMHrJIhpm8)ZM4$U1Ps~6lM@8D za+=*Ev#NO1eO-DQHS(^{d*u&{D(?D?3u4l{wNJH7<%Cmb=347i(pee7e5?KR zojy{UB=ape7#Y2?1?wytNs6A`NrMK(C4em=`UM0zxP8&hEIAu$DXmx+F;r7jj-L+8 z<*;LHyg)ISZJdmPdvjk)A$z*tiWw5a**G)Z1X3k2gvReGxw#t{eNz4bqXJHA(v|+} zu7X|#@g?M^6a|ma@?Cj=`()-D&sw)02zF1m4_?BkUMQ(mc( z-~QPlgcK+6Lrpo(OEi0}99oL?Apf?>!aGdFy9B4!uAw@)c$kjLk|k+KYL?~DF(_k- z*?YO(DjAG%d~%7PAB9U}n;pI(clIL(vv^uOP1thZ&aGG%n}L5#h>1g)u01uB+h*mr z+1g0)YkfY|VUVNn0Wp5WEkq(z;bGZL#pi9j{aR#jL12D7bcU38mpxGz&YG@cwc+NGG#Kkl2%_hL#Z52eeaehZ`rTK5L(fjk z2&%!Jhl+NE-BpG;GJHbkTU+Lk;CUDK+DK39g1YWN7Vp0W3g=d;x2nBi_YgK{7;wNw zi^peHMhaejK;4Vv*2=+bp}Dn2~_I|>|lp)!q9AK0COhj%#M zCyV|%wq@ocFc|5Mt7ei>F=<)2{Ptn8i@c*xaps`H*>ILym|@y&Z2*b!l*Ej!(EQAA zOQBN*wzixR?YBj&!t$^(ho;H>S)PfJ6$dlD)?q$?L%SV?P!9E8J&4NY?3v^@xsE#l ztoqXcmHpJ=Kz2yIMQY7W`)k6H;*IGnD-iWIN*+AT|1e-D5|5aITLWoTOQ9Flu86QIIAfu{wRhI*iTgcf+R5FR!-7bk5-5fMxqq zRuUQxc#>Y_rrgBkJ6_p}ICw2>XBnoBu`f|gZfj;JE3-g5;`3kz&cfKFX7c9R2?}tQ z@yRz}Le`R}gy8soUm^Ecz-#EzoQ`9N(pOKmJ_%$CXi>d7euNT$N7mL!&JxI@0fVYWcS5gk62mTH4Dg|pUa8lh-OEh_u!wc-$F8o~f%s00 z>vzbPikK?2JKb>@W`7Q5`^$dpkT&s0OMs3Kq(jWd(n6h}hz**53*}72#&P^j(S#~f zU4<;La<zV@N;sH^K<8!@1Eotxn6EX^z(VNJYW%?zZc;%Z| z3&PsNS9I<27w1_VC1u@D;`TG>isd7QC~E*&gC!vi^c|qr8%P9m|40T;Cy+EP+)0s>D5_kf z`4+5|X^xaVwEq5uIr{^78V?V%{!GH{jxgQ)^8xe0jKK)*>}kR8T;*9FpQ#O8S$9CS zcm8U$q&9;8hmkL_{+4|l#^szicYQPeV1H6q+msd;sw?{lp3zmBLo-gKue2p2UdcB3 z0U*u|ew@se8kk4K{E)KEa*RkYgO8jPXVmK}?z|(y9Ac$G99k_bOU!)hsZRd@2&ycDIwu_<48DZRfGr@R^DYbSiN#@` zm^D$%4>FHyA?v)w)Bq}XF#9b7wAhppqQTN!maI@uZ zVUnPkyKf>nU#KW9x)08f%2dPiN+^a0U7acJ&2n+?v=)}TK4Gw8RPG_Y&!wcz%vTvB zG>$AL_q}GPkOIPUau|AUMH_f~O!x%`%yGnu!>*kA32iK}G&v<71~zDiOIPscRp*Ep zMniGjOpxIPP5pjJghp8#;7`*B(`OL%eH$L)%Mo3wEJ!i>XSIB4#pMgDvXcdIhU6zo zj1;WbrZ~qWSRn`=>_r%~nVzUnTma!8vGD`Tc28rt^R(;T8YOa!7`ACI7YQoQCKa@Z z!G6YX=`{q=@&4!JqE-Tqwe>f$pI+phk=-VE@iN8T^I!@&DaH5lHH=n(@Sie)3|JnW zqtDr$R_@JrFlUxLAsH{4Vs{CVm=OY*588_*=JTWSHE$?KhNT@mlp0V#ZDn($>CZQf z-2{I3ubpik)@j=TX_&}z5=&4As4%8b4VWdLt zgc3xO++`nt!TdF=I|5*bF~zLz5_XGh{oe0#-z~`B{RQZ)tTq%^8NY8Kf|Ka~7%`8J z+9Z~#lW8_=EK>$|hAVF*!;UXRhaP~c0 z)~Fq*&L*3#L)4DJY#rYT#pcQwB^(Ng1JdPASDj?38J{p0X4dY-m~4Bq&<1(gf84%F z411vaqV)9Ujg5* zjp)l0=Rm{&v8dE6j<4_zRsCEB?zhaS@_X>*2=PnliVrW55hY60Ri0|F1n{-Os+M?6 z5G<$6=e?#0)0II{MHtW@6Q{q5!*Gt(gzA4+D}&^rhwRQo$&~kd8FLljYhY^w5TL*9 zJD@q`vJR*VrA1O7$0L=?k9J6Wdkx^%;TI&}tme99&7rQWXDfC8CDUWM0vTXSpcM)# zYPv6FL!(eYJ(hkDfQ5EKGN@L+Ev?{=eG~JWgJ-%%cn7vCpa>as>5wv_K9&-7S{EZg ztG`U7z`F0S5WMOhu^fly*UzW{>OCr86-!*<*^Q#f{e6frAgWKKgl8(*q%PYmn`1h@ z3Tef2_2;rbZ5C}933S;?`tWbPO%NlBz1fZx#71G7HGJ{wH{4m(TEsW$b0gZWYukt< z!}#dU8hwhIn5obglZ!YvWrrYRwjwkWHi6JU=BAlA1iyuh=fSy^-`=Zm03g9YVGPI9 z&}f0jDgUFg{s+9ke;s@zD2ptuZ;Yq#bl$2uRs$C_*NMW>?Ej8D-G}i>;`m%a8KfDW z3#o^sBUbo6P{K8Xelo0@he%JA9{2`t29pW?bD$aIGtmvMOI*Fw!N_X#!Wl&G#7%m0 zXPyCE@)?u`E`AY6IGr6cfEG!{c+Z*1n$Y-iA4mJzLllKoBJ7G}(!Kfzag-?V@1_~nqVfRrS8`#joC!%~3~PIVgy`zHqifm$IQ>nvAbq9B$0jjA6atND=L zS{<;Dg!XFt$xbztlpBU8LdqvUWBbt#=Au0PFcE{H86YNC>oG>eHr!dN2r`h`)ye?V z4BaOrgh+?t>X3KR6@ylYFapEv&nYJl;c{$OeTe+Upq+;J|Ag!>zWuKbwoQ)NK)_O6 z6uII3dUG&cSM6Yh+M-iwjHe**+@K6tBO(i0zIVTW$fo%K^5>zC-I9$u{xa7}6Tz2t zuY*WQME%O;`FL+^7I+=~OiuF6%mXgjh|rO#z1vipYYyBPgWtj4`8^92+QrPT#i*#t zZeh}_onIuavbXj494_bFtXlyVncmw`LFJ)A`RYSXeQnF^wGEJ+ae|QDYq5k($Y)Y5 zJI7DmkFA3qDC3&kn+HuK`1g!s%OP?c&I^v|1Kq4$15E(jiL}8!`?1ywosuh5D_vdb zoRNYyTNO7snbWpEO&;P38`bZ{v)z$W3~BdMbl_!jDVJWsfj4tEN85n9hxk$1*+xZDRn8~_&+Ue>PK_@0tHV}C}TD;x5`j5*DcB1Coi?3TMt z#4@P`95LP#>larWCo--UPv~k&4KS61!D_(vxP63GF3KbeGlH)s$MZ^|ToZcY=~>Y% zK=T`yx@c)9NSj^44JHT^$h1oUaEr1LQzG)Qb3UMOi8BNd5@_~QQuCC>knS^Q;7_iV zJdNNr3u2hs&;X1(jC_i8IPJJQ1%IF!-@h_d_rHao9Ee{bn9vHCt_>_OECA9vSyyBM#AtG4GsP~}&-f(zrcPV%F zgU*le=gU2I9QI-1=#)5(MJ#r5dcGD{69bI}iCd+BKi3|C`t9W?E7I8}D3R$TfaQ<& z^f?(+4b>kU8frcGz+hv)C81AQjw*)mkE8{DIPyh;!#X3|Gb z>|e=QR@ z%r780jzUGzS_W1NGI#X7k0%gqNQjk=IB}y0j|7vj$u@J=?PSPL8Zp!?$%QZEkj>1v z+a=>a@}5ywcn=>utsMrI1OO7pKS0aD`!|2)WlJ*byjdWnZYKh2Fawv8Hv6_pJi)jn z+|SwB-TdQ{k$Wb;^!KV0;Z>Sst@9iQ!%X^EYfB>&_}gTfXr5HdKGM0YwJv|kQGqYp z3`1GCUOToJ!Q3zwo+ARzixp)-D9^i-D*Yd9nU`RFb58@q7ch0&2FLokwt+a`B3x+X zb?|<87Ry7%1i*Jyxk^m!IXPI;aV@anK87ft9Y01QfIawx;RXuW$PC)tGO2Bi4+`bY zP0kutD!PK=GwEF5&fJm9!aTsHU)Set+KjvNXjUjzIvW1v*d1CnZK!KN4Hi8+J(m?7#KfHx^>kWG|zuz{07*AHYWrKm5@oNLMLsW8Z>#|Z~P#C{}w0w;_ z%d^u!rX6!QR95fY7~V;(^|7-M=apuY-eRBiE}G_pYTxQ@cc__A$Jr3+FPz$J=YnZU zK3v)HBAQfcfg{$HN(oT0A~G6A8O0DEy$XQe6}uEh^_8tCXg<2zmgNpP$6le`D*h`b zjsJs5`4M2IL`)wE?umpzu#}Alep@gm$4WXa1EmVnXGA}!4}s0H*lcbnk9*@5{mRuA z(b*-Lo{jYGaf5NpxoF50#pMWQJ>sO1H&21n^TGH%Ok>6{Cz+$Sz1RTOVm3#fWQ_sm z=c2jb^Y5ZiSh;*t?mAh8e&0M_O@gAzvqkX6vo|p`R(R(cb$ms5wUQwh?YwZ&ocSR= zoseOy_>0K#p3~hH#6scjKMd~TJd)pkM@ZtX9w0)Q&^~^Y*2sY z>|fH^WJnX7D=txi!uZ5B+iYr0s+trDOs7or*VvleYUdmQ|1DbATOfF2dN~=tj_Ad4 zN!w8NCr$P#GlIx^g!T#k@S(VyCA(Ab9lwqyr*`>SdB1vynUIkIc0llC&>Hw_>b+bc z=zwJNcyTS`%(C$$A5Q1O31^n&P5}xqV~7{l^3lZu3c~z(X-PrVa8EYvB0p!aE9?aS zkqnftIeL3lTZ!r>4Fu>TRIUq6NKU^1jUu_{&ARiPvM>J-2w$WvX4ie&5@gHY|DaBG+ggpFI1d$}P1mPn}$kNG&Py zK7&!lVIa-&881lG$$b?bgt6>>uHfnKsQf>dzju{J|61isEuc#o3^{d)GSpPJ9RzK9+@RSZG+$6 zR(zn`~w6|AgPXBw6b% zgZi2>C10|pZU$&2l`d~b?DG1cq~6Rp(g0g}qz-356r9X=Z?F4TRp}4M0^hwvKUxhcS;FWbrDDI6G3DC!BvX z8if?=H+eyA5VeoXVDx3dy!i#n}9g>Il6F( zw!Y}7fS6bC&r!nt+X`d)_bAg{a0zfg0(zSf(%(%Ljz{#2t^~7jb>PF+_UX3-^0}Oz z;OMODWjd5=7{T>Q(S^NoHPWdZ#bakspGuVE5Ka?U6j~@ocGzAy-jUlhkJ>xP6nV`q z)D|x=pq8;eDik%8e{h#RriGJBs2)4iKA`k!I9wE9e@0*_ge*YZ?Dp&Cj{m`*?}F!;H_Iqk2FEwYnXU4ye5Y9PNon=q_UG2) z0}@JIe8C0TWPPe#htX&d>ss=>WDK+5{7Sv1YK^BD5z!McFKzlI>_L zn5JR#`ZHl*c>Y}Wk!s#jXB-Bh4WHEEJ|^`ETrox zbw?LgM1Fi1Ho)Lr5Vp=`O5tRJ-0WC86zR*s&$sJ-VRI1{9~ z7zMlQLS-wC?9?{-4~ZPW4%xTRP#H~KQ(&)vKo&W=jWO01uc+G)LkQ(nr%Z}{Z9^{H zf{pqHbtlAFQ}Ih*dt=mhGFZ^qr1Y3Rch6k7{6KNKALh^Rk+JS0v($Z9;WHU;IM~MR zjoLs;zB%5p&1}Xu+VYbbzuSJo_|BDZkRnknjTTJmOY)i-DuKeR@ugF#G+W_CMC_eR zJH-Vuu5XsFERW1bEyRpWc7LL|(0wT?rnugNUfLK5$*vwb<^M`LI<6}LIo0cd*UCSC z-HRuCnK36%##m(Lx!ev);Ag;ZD+M)#w z2yK%ukITul5_D#)r-O`re8V3RQU8!Wc;RZkb9*%fp@#kz$*DPQG}NyD?FZ0XW&(H6 zFO*-}`1Q*-Aol>Y;5M#B*O-EuCdSxTf^NbK9eU6A)%WmT6 z4ugbxNalr5i2zd=Z+HJ%e^I*{e*D_w>hb(Ja`!k8vXR7rK`<6~;O-LyHl#Zc=8fa$ z5e6klOJt-jA^<8cRzBP@5x~@2ZJ8R1 z&S@ZDJeCm{<;3e|4k~&avayxK<_-?F&Q-tyn09a~9pUjM^d4)y9iirY8j`UH`3jqB zkh2jZE*hu+Tuz~604}N1nRVBRWtV~i53OUI@#{S7<=Wg;lH#__Z^Yh zq)hBbqTE<@-XD|suI+Z>HDHg}22enTj5e3h8E@d7C$Z}8J!h~a{8ozHtw1m#x$u)K zUd5l*0W4qwMT@?-^*xD(R&;}+u}h7uUT3WC>Z7T zpcaTko*0Okh3qvYbskhTnzz50H*y@GyMx$xRelN8izTaCWBAW(5pkKTW1@l@LzUbk zb8HL?8%-(^wokMEf+kVXrf@U#d=-SAby8c%0?61+S*?}qI2C+cjyij+&;ddx;ox(d ziuK@>M}KopUBn-4Pa#_q2NtLjnDC_oJyBEZj|}%c0E5B=5kzO9tv6%$cIY8^f4qRF z8q?j4$R94{Ix872qWH4j4d)q5HthPQbi|*?BY_b^s7l2Su`TwYX{0m%-rIq+*U$f7&y>AwM(H+dEy`SQ*C(g@O7%1g)vbO5tovd zb#-|367*1$@?6*)tM~Cy4DxXZnZDzXuIMHTSygdz&Z9E>$I{US<1T(@6K2~`#2dG4 z9J%SnkbM(nx6hHKknVkvJOQ8%Te9q*ZR`)#w`wJk*81LBDf zu2*uhg^0ZE$E-ivRMc%p{>4%Waw@Fb^fQLWHn-#(+&29`I(LkI4u4$ed}LS8zQ z!tPdJyGRJ;hEJ6l^6PA#3OhNwM>n`~)DpTwG$)JFUhzsa{d|P!_T9P94_Hp;`g*kZJMMT_+kmG>l7L!SECMw#=MWv==1ZUk}sfLjj}VR zgwQ;##h2BykJMo&$IR$vzMR!Q9 zGTT`YrhRs>hmP@o!J=*NvF!mPm}#;&0J&IlD@Ns`6b2RB*|_JW8hp+Zk!T=;RuasG zZ#Lhtb%L-&)k!_Vn3=74N5vpm9j$%jiSAN^slgI;Ctl{meH$36sRd?q1r7O40`(>( zRUBQnf|V~mp9(1RGEm2t62B|v#-bNGQplj)uo(6J-pA~%uOJTK=@ z72Y5e1|*dBe|JcKuedJ8G(KVdd(Q)J2siqMCYSq0)e~Wt3+&iyQ@>)zns)r5I>iU6 z!mzWogkqN>`k`g;@sO;ilN9RXe`D3&BCO?cSGPhVFz}xuxYNDRRG*~HR6}?RtOH-C zcRGp!ZSuFmD|T^+yGV?wSMiYJvF<>iG%707lA4gG$^qOw0?`H0=G5bKWMjY$;J*8# zb$vXML|qIXn_mOgJZ#&_KOjc@&Hi}1HeO3lA#*)a;u*o0;e0(}M_ zQ+Zz)1K=(YLhD^DQDwB$VsZwluewS<#9rtuUL^@e6~cmlx1=?2IAK8$;%Dsa@4%`@~lVk zC!@VXMPW={_R1q|@)K0~z9b$J_Wh^e?JrO=;B!QL(Vie9##GNZm2kW}bfYMO&~aHm z<>)otYVr`q7pzJlD9`Hlm%+9rpLG|gBgV)my^5E^KGo63sTL2oTDa+YgG7~immcCD zcWYn7;(z7A(R)$kqXZ?=?MIWyvU5+KvzIvpJ-^m%S09U- z6*{=vSdSG76(}593lB?XdQ84o@(uY)A!V?Gvywo~$IZ0sS04s>VoCjHEm$b+@1Aua z0gfhqf74?{p&4s`&I^AYtB|maIncA;9 zk&|RQvD1Bf?AFC<`LNkKm~crW) zfJ&L}Av(g>3>+eEKPryZ<5vL9c?{IF^XG7jvi+wQ`et*NhS$fAE)Bl}Z+59Uh!!=D zvr+Toi;|e3<&C||c_5#pv8o3DdU zJ%HWp`*p_A-J>5ouQYA+f|xoe3ja~l`(McZZ=1iqKXjscO*u$bPu)69qg%N5 z7D@?TG3>0I;U9R1`rXAAsGw@fH_mM{sipQ?Lh7#17c4r6M9Mv6h0J?_V!d*Iqfq|{ z6Gb#HNsSe)nYilu=%e`sim$`%)WiXLk(vn+4y%=zr0QJ7ycDkJ!iZ}7EAa>3NK#dyuL)y}}5eY7^zo3zTpy{nOsRj=UI7OC9y=Np;$mtcFil+hSB9&nLdDHEB zEQy`q>N@rgAFjaSkj*4~VLnv^oRFNqpNu(y(I2*oboow`wzQnoEB)>2IkZsX&QRnZ z|G~ezZv1Zn{qj%qhKBVPO&^2xG&tOv@e!D);O?N#t4Z}F8U=iLMRzwowrd!Y;x>^8 zFU-i^qdxASL<3u+-)=5s6;lVX%QR@??-q%|zo)!ScF8^#;?Oq&!<;tey#Krr^~)xr z=SYI9phcwFr}Owa>`gfeQnwnq|96ysA{p3%rEy6>;?e+MfJ;jCx-&vVjmpPUUV*Ov&GvAz97PV*q$b})jkj%LAa z!9*2@^AwCPq;y{2dDeT1I}DaQ3BrB`csz@u&JBT_5tBK5<`{NM{V7vm>S>j7pOedYqAGF>HODUv`mOlRv zU*8lR%GRtK+qP}nwr$&4v2An3wrv|LwrwXXPHy(T_l$ea|8V-HAA0l{J*&EA)%*&d z!O~ZStiQ&8O7s6`Z~&prVZlzuj&=B}pg*-7@N;>m{^SNDEbYg$PkPP=a28Iybv?l+ z#IFdUhu94pXKz;P9w4f87%7S@lIRxmpgephdy zl+KX41oYWKKh+*w2@XJNo50!Ftsh(xkP>=6ro36V*nKXV678l)f`CV0pV*Y98w)`1?ihHb=KneTY-&dhWz(kOGRqA6kcSgUAYSy z-nP)+r^@fj!P?MkD&yUr9u-XMa`p!faY5^mO$QHUkuU=Y;94CBU{m#1)Vw%%Jo`<3 z9V0h-*K@7WPed!)48cY@6WMH4Zypxcp_cg(_bM&MA+hoe23W;;o(%%XXfk)E|~>L!=CPH>S%5ET_r49Sv&-*lqngS z_6!r68%g>%?2f-iOdD1lc>kODmi}?NN?Qt-=%@ogdLOvn+l9(cT)Y%i9)r0e(##0m zd~e9EY&TZULoBQ}A7Kh1^Y38&gVAcA3M(~gC7_;m&6_EIvW1d=;n8FYy24bBIFAcX z9s6pAu6NVgY@!e@PbymRR{&iOvzUps`E~i(Q`Ahtb+@uiIp+viFUHiizvFUC(DXP` zXWY^0(>SKpi$z-ts+mQ>GA{mgHzo8}^4n6#+uZcS0A6_?V0Gwy{b1%xca+W*6Y08z z$1B=m=7OtDzBlpV%adokF@yWyhhd`{`j@{J8}$P7Ov>p&ut){DkJMTr%#81*w2&YL z3d>x-o-5&_#1x@|R*+Ib`M}cBm+jJ^qzlTb|*_cRV3PC)jL?FZdL5 zkIiS}%$NumG=#b3-TS-=$|${P4>&|=Z)giLSe2sYP5M*Xa=<}6!=2T)JUF^aUx3V} z((PsG@9nR(^z}r57+Dx?Bg0jZ{_Z-vVZTC6$PQ!&b1>c6;7&hyFbpxJ1`7L z|9*ycgDGZJqdRMyz63sTvk|y}x@hdrAcd51xRQI}pZ$zpyLdctgL{YvHusR1MhYH&n}G6m0&sTJ1wdE&3WSW&-7j-#QVy;yWs>5# z7(c7EqORO*4Jc4c7>9&^3mwfk#7C)@y(lGdOl#LKx7HOPMoIq}NOVyrTu-M9kpG|S z|L=Ul20tbQHT8biMpKsGL^qvjA4?LTlORtrxMd_z^EN^unyW1!Vb9A#xz}F1ahGNw zUq*d;x+eMt2qfr7AyYI;+7IrsYIg*zZV@a(OAPu4T~!^zC-`~OW_9q7iy3Crt^AO1 zVe!%$#}~r`8O2@qX%)64HZTG7U&bXYVGx)y(s&XvSz`HAx6}y~zrRTU{{yD{r?KoG zNEOKczldZYe~4r<^nfVw^OOp%rT}7uy5-9J5h4Ho3jggb6ZnBC@jRHZ#8`3Nkz?kf zH%A2UGkxPS_xx$TXfP+`-35ML0O_&594M7%!%Gi*57Cp{@xBAtA73VCijG z7;XUvLj~{4W0aa^jEjF48)#f(#hQ>R8nQUV$K1b6o^$EBq!>|}v9OKw0=ELFmghNx zH$=UDtRRzS!y?zj)8GqML`oWr22(pcNU@V!l^EW0{uU5@7thC^U_&2M>GY8K%RkKn zzv&~%+X$_axd0o%+RB85lE@X@#lwGR`CVDj!F^Ia_;+oiXL^y3o`RCb`9&UsPF3;F za1MdH32~jJq?TAP{=I?iyxxTLhwl(X{_)WQ2i^1?eKRuzt#ai+%thRTAKK1%M#cYH zl_QBx#==?#`o&ZcufIy%8@fdaG1-&>XESR01&a9*WF&P0yf^7@W)3u&Uh*Jnd6tYf zK<#KmllbYOz-j#NyT4%>#J$%>^EL$%_>TK3z}~~S{d3RDM)g`wRYY0A_~J9uO;J|z z=*%kL15w%NDZ$!ZZsGD>etuuQUh?RZ0rpqOaiiX@{gCz z^2#3iyN?kwk0m(+yc}xZ<*he3+p0Jkzpp|E1RM@z`($7mFw8&Z{G7H>maf)-{UI+q z$OLt_Lq|XD__SO+6~s;#$w$pMIm|TOb9w+r*Sg<#9UOue`H1m)Kl$jeMOB<|gg@@y zOd@zCCG-YHM)9mae59ykPkBxNkPC^Lk;^E8A@H}-h_M8_Mq~6DvO>C z0ExgKFIlt(AeaFtK*>r)Al&teMI#CJp{CpiFdcJ3<%8OyRu4RboOpKuq-HLtHGMv+ zb&Wf1ZzbYpGc7@6fgGoRkiF7N9V3U>t-UD?BOV6SI_;F+3#gobim2hiljIDP%j#6A zNRke#tPj+>3y92X{Q)<;TF4G$B>bYhfdyEed5LO2yQe)FzWHGlM_*^-m77)&5#`kc z3LhSH%u2iD7_`}Q>cSWrfWW%;`3udauiycQF$rI#tdYZEWz7j^fKWi=pZe^&Kdir+ z3LFWQYy2Ke?h=nN-(N{)xS~T0->x3vPu9$NR-jB5QT;YOwBBPvo@jw!m+vHJY-X5W zTWY6bCFR#|-(5Yl&&n6{865&^-fJi9y^kk}fZUe@3*%y2qHP}i1hm94c2zH?Qz74t zb%3hyOwn#9bfLHQr|4Pd(=}35ql;Bp0Wg*OmN6IJLCb9&cYwz=FD*eJV~3}2(F|-i zH-bez+-|e5Yz&K-MftCE+Rs>iBQzUU@A~=g`BOMbb_xictnn>&;Y?aIf*n{K^R-Kz z{i=DZ({B4TsQDagoY-Rlf1%%M=RuSx(BRPvyc26`oTq>MWPFS-r}bqItbG`yhsrpN zvmjnXB4OtV}1h<>%Rv@rtG^#WYD71@o&eRm@) zE<>o7m~y+3di8*Dr02ybZF=q9*?+c=sSpP19s@1MeVI>Q?kJLeIK*gd)F~?*YIK5S zw6u2+=jOOEWQ3t$DhjY@UHX%-tR*6Z-ep5l{N~oTANOv}tWdIv7X~8fu7ay94GGd- zl}O@$zOjKO8rhI4wDNp47R4OHkaMfeDr(~egI{RRHw{tKXyEF3CLn$5y*;43mS*j-%32X+6il!>DztB~!q zw|DNMKa`}Ov#w@tL2@r9=au6({|a|%5`lMTF4*2D*PC8oY&w9Wp^hLG9%bUMeTSmh z`xQ`9Irw*VH%TzkAwHtIBnK;@cHiHby~25Li*5-$@+Tl4^|Zid>|H(&M-fQYV28{< zv4yV&s%xa#ZV&hcUvbvwcGeCTxiaLFz*2Nia7OFUEHD|(P!tv>y3N!u^6I*>pHK7I ztTdb(V*}WGNKpkaZ@z6{fUG>l)cu*65eFG585<-kUDX?=qmg-)KdtTItp(WiE-J~p z&w?acW#NSasnb?i&}U)sZkbOMJM4Y@vhElcofo@qv;c?;eo$GA(e{1G{(FG!SpobZ zOqo;S@!K`Jh)!d<8JX*xp8-mtS0TR}cP-Q;Oof#+s9}K;$Fxg_kXL^O9L|T01YPRj zpAg6Q@Xidv(e?;e9@DQ>x!+rrb5kIHlm0?!p{>TD*i_i3Ep!qKg;zRD!88(xKD}fr z+G0_zA-U)3r?&2jSE?Jdr0%%=P{MGMWa@&EUn)w)t0li0bse~9LWR4|4jyq?s+3Q; z(tngooKC=+{-)84Zc}oqk;UH!o{Ab$PX4+|pXq=eNcjBaB{P^L-tW4%hD5PdVO$z$%Q+6IiZdWZa8UIR8aT+;btknUCTgWnfUV-_y{?-SjvhJWS7yoS12_~dN) z$>SyXr}9>S0g(Hehsd9aLE~2W)|)&7L#Vrj)@FkmFTZ!`G2qaTfW6{rJ!vKoFF(>R ztAYbF*}I1s4C|<t%DS+aX+ZknrZKRE#c)s1GynMD0R91AUe$lDTqs@mg>i;A20`jNo zofOuFc0Rta4Ofoz6F_PToz?{M7oZbQ;|aiUubiyp6J2;S7q_0=3E9mdx`&jYCo6IW z2>4`g{E7Umn&RA&W#E-i|FZv0`1DE^45aUF(q2uQLgJ_9;VW$Hfi?xE`ysGn(5CDL z)Ss?C&b!8G%r5W@o{+OL29=-kOlmHM_;X5r3!B3hwbu@(RJ2j6xa7*hyOH!EXm5<9 ziLyKXfYwunGHfw^ymFZJfr?(vUEs&+Lt~nT7uM}jqed_U;}XOb7P&q?R*?BMnOuhu zrg(V2vm!aO!?xXZdZ~X^M2kl}Nw<~cHL+?P`tK1}8#MQML{s7wynG6jIpn!TlaYl` zC3V;f`RcgB`JoDX+jx_1U zYzxG&>t5|{pr067BcoPV%qtaiMQ#?jkmbJY=MktT1qRurD>-$6K#YBS*y?7C>e;Wa zyf=h3W=|fI=nZ9?{l^%}S-m)2Ty=tx6c;SQitOqz<1oMA*V0b&H)aL|Clm@2y%)#Z z-&Stu<;W1<>*I;y?N@joB0wS0#V*E*jIQiE^E&0O@JxdQ80Q9~{SGT+1(|{?C(=Ogms84(9kO2h1T=#<(^0Vi-X zTFT2trSPkA2E<3-LUGm!9YD-~DuU#{DFQ%lMyOc6s<%Aw(O(b$5*Lk~K`vRzb~?LA z9VMPLVYL7RnUhB2PMAP;_ku_I_rvEO`+T4>(^j(%wYk#PdJt#^k=^JHeAR0-C0(Ta zs5Uk6-gT5-AiDd<&0yeP7S_xRu)juA*?z-Xc}V*xH-CYLHCG-J++4lkNOjKSk+#U( zSe&8fp*hL3Ok2XFZpB4|NnGAIZ7IdDTLufZy+Vy)Q6`lrL!RV|at^oVHGg4$t<5?B zXCm1}7#o+rEll6X1^W7`ZJQPZk6C2x*zC081dAc!00aaD0${bvPfj_6n&o`9E-qrq zh^|{ie3PG}ez-8|)a9x9D|hIe9WgQdh`j=t|46dEdpDENO6gl895dQU839^8%IL$o zmI2gFN1OOT5Ug-j6k_}6*mf85BZx-sYD(x=yh?Tl&H4u~!9l8YGplsEP`Mdhpt=*& z0xb#RlyR{g@hP4tQtFV*n#d%axsjr@h_TW=>zHS0!bBIbCnA&9?+!JsZ?%C9|Af$>REp%t{4+ca54dKf$lvIicp+Vdp<2a5oMO=j?Gg*Wt zUo6o_n8Kkth@Q${17LA%JWTP@kgbY$a-h@P>c-|Fi!(45VIcFgfiXM4%;%1?;pV6H zHFw_f@l*T@k?$g?dfz2kuJU==?{iNiYbT+vSBiba8ngzg$!?D zI_6%g$!iYe?1#F)o1G}fGas!>+vrD}St4b4a!>{)(>p%D_#IS74fXlRi+S4n8X=Iy zZ4wV8lytsc&;^+Hrtopre)O_B58`5IK32(463y8kcsqD z({boiBnSZ;{*}zIEg`!eFIVFQnKp=%EKzG5MPUN_#YWqetzRrFi6FpV9jQ(Z z2w~qX3&*}@f7x)XnxBMEm@VvuIe}h7Ow4o&TVHVOw0Va?c(u9YP|~1ZQO*7QVG#3{ z{J+-ybN`ou806o-(qPg5-v}WFFdxL+)0{0Rsyq_93Pp1twI|IvTh(fzu4-_8Qo z_RU@W`Bv(1yQQVgR7=1Yx1qq7m6@bb);oJ_Mk7&o1}kYG*#5qvoP<=z8w0Et0`)(o zPzOGVjJe-Q#z?s74n1-ygV*c1249$wxW%m>rv{l;I}7B~&?=@fY)9HRO%`-c>n2ke3{#wm{rps-x;pqF-shWI^2 z!z#cS{HRHSPWVSZ1BGh?_st{c3t%bVB6$~aqHG~3yl(5o{zTBj05Fa;fso?bQ-uvt z@9?1n6)z#uCuGFEHfI=$dO}Bqbr8?_BW0xm5*^;l5;2`gEOI3LIh+tfHLh0Qg$uoH zQxYBY-)+AxJ>Xk0J^ED0jSmR7!I zIJCqs!b)xnCGpW61Te$(l7i#br5CYo(?1gK6yt>1E!FdllUTcOr;cSR1QM2HF&3_( z>}|Kx83?|#p?ZuN^^=wwD!;vM=L`7i)&j!(yjnBqY849oFS!^5lY&-|=V%7Rv%l1} zZ;YaK)FGFT^Qc2_NinOe?O~K}KUy?vV0&$-73a1?+PP-#;R8So?t52XW zNoVYXI9JDg#P&Gq-4J`0!|svv*Mg#^4ytqszVR}=1DM=NQ2kED`4ZV)7R(egg#)6t zXze(>?*{ke+u=vjY*gGvl?IQbl4OrD+@mtZ^*Af}Y?H2*~7bb?puk zoM~>mV+3MTe)CrOrfRd=CAXmZh*1aK)3pw}O%M2ki0v6B9SOfT`Hi;#OG`Y>C4>!HH<*4tt;q9{~R zOL~|$x8b_WFU2~*EeL_e>C!EFb$zMtHOU2QV|b!YoY@0e(};lO8nIifij3kk-G8^` z+`9ErS@ud5fEFp7Rgaslj3fs1sJKSWL&Ct7na(FTS`~F$-T2 zjRmeNYV5iyj@Kl`A;tbO4jeqOEa0m|ikNa|S>eKjl1JH4^GR^WNX$g@AOoZ{N^?;< z6Hx`Woo(N^>&RrE9Y(gWzKs4yd{Es1fXa0S=mlF*6W#KdfIFrw^j<+vp<-*r^9+T6 z5ovW5KC+E!Wz%{Rea?EEggZwa(X~vo4T8$3=Qd9Oj-9*RV^}f`WBD@*rVvgnn{JiC zII;IeJ(3ny?mS;2#X8oc>><~QTu@s#6V(cZZZ!8mpIMI=!?&QK>!443K6Jide;v%q zA)tMqTr#0x^DclTumSfbmJ94`EK6PaV@0Fs0FL$*45h}Avq1J`R> zS@~~7lZ(v?N`y6-(<_Bl$sV##16t4m#=7;6&U0g`JsJu!6b{MeD}p-cW1Ths>c9(N z676|<&-o}Xxe;*Tv5Q}}LdVx*0>y^GS}S?epy;7Tii%dY6ZXMW`JU&yjZx9#2l&Jl zB+6M8Ruthk(B+kGz8E@FcZb6bZNc-8cH_pOZczOBP-?^=#Z^sNDn*n)d=N+aM zoeA4twl4!V|Dv09N@U0^FYFrDp{|Sj#5VkOyCD?tB1Pe-K@DT*z3CLSuQ7eJUoy~k ztL2w-8au?&;Pq^I&Zo(V4L~*3{J1Uv_C`+|AU_bzQ^p=5Cu*1?TM`o?2ht=O#)q~Z zm~a^7%kK4pggaU$JvkB*&VB3|=6Ajov+^hZ2A|2crF;=`q9>Kyo%fqItSN0sHww5&G?cgBdl1~ z7l_^QsqRC5K(P+azKoC%4dr#u+)%*hV#%4)_(Lyo=fq=+KhH0G17<^33%S-yAXt&X zmJekbztRC%+*-X*BUgbXQYIJbjgM}uk$+|s-U zlk!=WYL&RhUvKquY)^GRE)RzDHdI>%QAht1S}p5(E2? zTEFM+y->8DuD#cq6ODJ9OK!E`FR&kN;!jD&eu3R%!?)j&POZRnEhx_8FqfLc1hcL0ydpulYDnY6q=I|z-WG^hQmLWV5BrjYT+OG2-|5m4v3#iojup{5#4##BTv4Um*{BeNw32&(y zcBGz%(zqI#j`9hcKEYw(S;Uqb{rzF*+t1?rs;*BC1y&s*CsHkaGn36`Qy+iLqbCU* z73cm4zx7G-IY?;Pbh6k*X2uDu^AKY&?$INgaJQeing>@Vdx9xM@;%ywEadq)WuKRd zoeLloSS2%tS)p2^M&b&<;oI*Wy7)aJ1ChfRBt0{H%rKs@{~c{c*iUU5z>pB zv(9@0?W<>(O)dDD#FA5W6qL?_@J{3$wbv zJi?={+mXjQ;5~ukjVXklY2FtQJtu6ilcz8|hI>j6y^~RWBUwH_~90 z%dqb?q%c3`I^B&NxWB#S7vCEO63KLDo_fNFF5>sOScx?Q%#?=oZ0`8&Gi17aSu;6< zLEytnQoql?Q!9olgcxLWsOTS&PPO|4l-1HH_?=5y8$!=TVOUsS5EzM==sI?y+B#dt zyo89#tr2|xc$TXsIT>6zhFID>I*Z##C8|%Of>blx7}^8S0`40`U8&sRWSF^r?UkQ+ zs~a&F#0LF}-C4M2vTKn5`78oi-wv;{J6W)8>r~msM4ro&JSkz=bsNv?|HU<=b^f=D zjap?sdyQl8UH)n+Gj6C>7i^@mD}2{pdR4;bC)$fm<3j~rYk7=??k#O1Z=SttomIwgWW@yv0?L@DWy#p)bHwv%7=svws04Iq5w*b|YJvo~9i zvpM8-nP#deP~aqyI?x>5!z$t9+T2SC{BgX4V~%qQ_|S4P6PfBjJgqi_B}AbM(yf{d zxt+@;SiIb5)MbLqmGN|V0!sze))xDEaBiZjK46wt)wmGqGVv-!Z z*!DOeMi5_Pe{Pqd67X$CxSl=Ht1zfcuZ^Vy<)L9t<7K*(dvRdGXfR$y;ZVdJIXXlr zXs@8+qg7(W#|sJi?yobW&=2^Wgq}Hy8Rf8pD%(fJ z+EuMkLQZMp&TFG@=waOdLW}cW_Tr6--N`vO?7P8bvb9^(#=$nxB<&sqqeZ*UoL)Os?fB51?ZaXWK^pd9DXxULWWShC7LCUQ{KH!y@VFHX8|D( zZgeDlc|;Zf@s&O%y647puqqFYx=F&rA3l3$rej+{+{Ib=|_6|1C^5J!z)wn`aK?4 z$-nuVdA-gjeas28t+o`m@X)sq+E@vkm&UfA4Se{}UVaNpK1v+O;cdPB0Kkd1LXQ=I zqX}}5SpmwI?)_@J$3ccf7>wf^wjA}|paf7Q8HSh!F(rT!VF$U9ojo#H6f!nG!jXSq z32iyYJNanqEObnrk%TAc^>U1Q_-J9akB?he5Vp)z?xYl5)5yML_#@Uuz0>Y_NnsEZ zXe2gsO4#ERsgdBlmeKFXJymC7o`ek=ulKl97DOZST5h!ou`-(uSSidEXE#OUC>Nm7RW%+cXY;x<#b^Z?#6}0KID~~02aICvy3jEOYG^25HXC%n-`9f% zv-9uFx|Hx+<-gJ#)qA-&g_9pHuwH?mWSatlzioy9J$%)xZHlC`D&mB5-&Pj(TmEtR zUm@4Q8}&Wy1T_0#9CV-L3S_m|aO(i>t{^YV5JE6KyBhcX$(SVxktkmn=L1ij{E+K0 zvQtZm1NF)QUi8rYO0J%a^ORy>R*0|Ekk7?cGA1ExFyUCxceX2UlwqU>o8N3>5598{ zioaA&m4k{GpMTNW-DXke|G6{FiL!FEpiF{;4Gh`rMzL#%SvD2)A2N;x3RKlQSJlfR4%{nME7OMAH z!@;c)_sW(lR8LEaz|UY$%5BX9xvAk3vo?5!DHXF|WxN!2jGK>6K~u{hR=e0Xv^|V4 zbl@mE6cMF~1`0d6Ire>>KHytjcw`{G7GsGHu}fb#*XJo?mjLm?7~fo&b8uLeszrjd z9-l(f78(cQp-m3~dT?`isf?uHbI;o%kQpz{sW|#A_HzF8f<(Yf?7wHO&HX_)8oM5TQ)~g06+z1Asn0Cq<@aj-D?6 zMH(Pmrx}J?hgYxDszMZolhI2iDnd^qJkBiCByETowZLt*n6J2FsDsp(=H3UYbe3DS zcU^)g?P5TQ&A_ZdZGYab*&BylS;z^ zxQ2P|5}@TxK5u}yvt-6i*aa)`%1z6MyT#+YbjLw@O;+^&@{4YF5UX%HOg*K8%V{Bs zEBOS$BBAx{-to(x{VF4Jg=~S#r0`8PiZZN5qaxN@**BY(B4$mm3vgMRNlA$QeJa|6 z^%Yg=Bc>P|v~P7Ig~=a=QE`*_OsAXHqkly?rtS@;{?s+c+!`C)WDBbqe^2=4={kTh z#XNDGmI=iU#udiJO$u>bQsdmO*P^k#tZZc8FF}mKjJ*_Xb>S8r^0yt=AUwXe*7`%R zc%PUfAMRBQ*eEn#DBNu={}WGPu-@rvY0l*$wHD<)K3ibqt=!*SGOePZMs$85163?C zgxMwx_Us{s6E2pA9hR+fD16blXCN9C(iv0tfv%PheRxJt+r z(k_XcL?Fi^VF@Owo+khIEaxXNRyS zvhE?)LW%HFp%Xn9hIF0l0t=3WTa0(`e7*iA|8U@Z>%6xZapt|P?N_Y}84BVvFh-%r z4!I_hT*0g6L&a`uf(%D1yD0RK`;I~FqhoV(f3eOz`isu@TGDC()y%flgCM(|5?p5I z$Wt*6#hAB4NS+LrK;G%fJCxtq9J|-l^Eq)5S9Q77*#ort@8+M`pbVdtrStr5ayhw8 z1yYYt3uL_j>T(ry#YuQ9T{lR@eJdQ(>{PH|*iR|Ulir;)uY$)WrHoK!dMd(;60lxf zk)UhfL9I4o8k-{8>Jg^1SsztJUO#jy`G9LvIPyXtOAH-Lm&n6S;;S?L1nllCvt{a> ztHiPdfHpGOZKSsek9iKehL~_VRU)=uaD55OEgHHGwKCOt0i4^Wj&_dpqAqibl)AY8 zEK>NJwW9l;OX;&*lRJs}tKJ|ma9hKf!i@t&e?&QN_#v&smKjq16hPl73Jv6V_ofxf z+x+Sz!;&)>S4*(aShKoFQy?=a08t}UYsr@A^i<7ce???jv>=tV_}og61A~q+MUJGx z<2MFsZv=-icwH-)qmvUSle_8Ig~2Z_YGaoa2QF7>oH0V2V3lqzj0k_0$`SsImI(Ma z$Wm180?lsPL|jR!4KOHpU14s(biGA_H%r$>Pt?gKW zRh7NQYhxnLubif?s8XxH7J6Zv!@XVx&lg+96U2?0WZgRD9k$}Vp@wa-aX1o5Fy%_Y z^P~lBZ3wdS@8IC7YoVH#o((H0^%GZk%JUs(o%cx!AiB8##FfrrpE`G8`k`J=VLAQl z^qWbAw#wN3-sdxvqa7L9{7w+(XYm0;w}zCOYA`aquGyBE9&WmX5z0O92T0L7V6NXT?(2+ojA%07vGko#{6OVp9sxSVn#o4su(}RLq8@RZF z7g3zIc`&NcDBIXju=>hlJP|Fw&Xf8MQAx+t&m({n#uhynQ!FkG-}>CN-my$G2X22? zD$M0q6F4l0o#d}wEnUPcUIrRz05q=gC z3Ub(dSlC~5dtLcL32Hie31&(<@D^Ok!e z;w@D9+C;hKtbPaoEW_Sr#)ZiZu=-jA7-AaS@yDI&p7^M`$C!MYK>fmqo%SjY=w%0! zUo?+)#luAtw+=LEKTt(b?~o2KXJs;-Zl8%Fr+8T4Aj!N?&AXndHZiB40oBbzhR!~z_6K;&)ySYU|oR4(_mUkBEH|H|6OEZaDJ9;xEeAJU4WDHB-w$ z(Y2fHWtiBTAI~T#AD`OnhydCF;>3sm$)5NCXP6K5978^sn5=c1$6{4TvtqE=>&Fsd z{VKR?sEmAaKI{B<@qf!XvX^6l)d|DF!KAQ6wU(cd&z1Yy&iCH_7*|X6;#{6E&Yl^o zSxi;BqL)KVHEJtK4HJHNwMbN`hg=KRJwh{1!B@m%&FlI7HIyT^d-l2{dF{5z(NSs# zD^0rX)gV=`CzLqfR?dFJu!L!Z{7J)-U?I9d(AK;nc!^t|5-qaZWM4Q|MaEHte) zlg$LD=ui#iak!WC&GauWxPeLiACBq#RNFDi`-hJ83#KmE_uwQ#OIn25N!K%paHl>} z;n;9C>gn5$0>LNP!xY|G+Ka6Tirqwwf>aZt?U@UQZUGvm&Jxv_=3!MO(bQ7lU5~d$ zr0G_*r`p(@jxrE@v?Y7kafm?Utm78P058zX>wvCVKrJI%K+g+>U>B|H-MV=$ABk^O zXBc>`EYm15TtQ80d*Sk~*w+!dmcRK^vQ#EF6L|Z*0UbNpLKaD>p0_4nZ`EqcHjY(u)&wp%ex>-1 zw~CRxUx`$j&h&FY-*dAF4M#NF33mFPXhMng3GUU#kS6zDl;;HG-uW8RWeqy zc^lwJFW%$4X7s})5+v9u8@VJlhTL8HM)MeOwEp}G;}Vi;iH*SfHFf9C($`#7Ovqd! zu7Y@-k=)r!vx7IK-*oN$Tjaich1TZ{Rq>si&hodT5*rlMZBOp1rkY%INA zWg}?DEojK9?$9pVo7S$r(X$eBJyz>IdEkpV^6)Zm5B_|PmhKvI3DCpxj2b+~){CKT zoh^oa3i9ZYNVuW3c*3JUc%3sP6-C`Ew-0Sd4ca1fq~HQ*Uz-@*4xYI(RFr@WuxkNx zVVMAcB$-l<4gjd}*Do+@ zjL0nmF*90>TM+w*O${ZzDhxMafm^PQ1cN06i^j{QWuo6PP_}Zm*%CLjvO1m zO1_?LJl~>-T08Uz*W?{pjkL7J*+RGmg4NMRTz7(OBhw6?D$!Fjb&XHk0i0Ld)8+>5 zi0L~8Kp^*V*5;ScOGZAZ(SaqL(VSY7Q}s@xrOjpy<x9$drX!9rv<<*k8hV>dZ8}1ZWK4dO*Bv!Xvjw%WT_bh1Q!~n-$v!O-Hm*Sx*>QeC#xCQa9YD0tX#?o z4jX*qgi9*S%|y?j7Z*)c4{H!_yq*|&&2S&I_qS!13sLd?x1;hol@(?! zzMG_SE5>nbGB;7J;(B?uO)=3oi&xs-O`_&sEl-~|gTDZ_%3Z3F3hP9PgF&&A^zY^I zhr7Rwp_;!8XMRrDZ~SiY^=PMQBf!u-G-QZ8h(}pzdV?;$rs){n#tlUq%be(ALGgc( zMBH&s3zn#Ec5|)%l(gB_1&U&z&bTuZmGy#(ZLP>4L`5$aJzw%NKe>jlD1**}X)w2v zbxY)@m_M7_k~&(mLZfzI2nG+dE@rpB{5ON;9>djai5;2;TZKnro2rd5bpU@=N4#R+Is zqSNn>1cWQl2(Yxxm-rE*H~S^>4X0Ubwc~92vPzr@L*7>dRw`rp2rU^bst0rdEny+A zvbX`jV0(mZrpN3ul+X9wkCpfUbSl^uIzqOa^oD7q1LuhQvy$5Ta{QsW*4ROHvNrby z_-T+A?v@%oj>U)gb==Sfbn)BreY%BV3mU=_j!;n$En;XXqzz#^{?x2AK#5Og;Ug=(2tIA~W3uMW?CB+v4 zVh;82YkjLH$?!Y$E@l%eh>dP99TDNtb@n~rhG>x`Z^N0W5K_~XwzDUH%{nmsntSRb z7%fSlHJz#!!m#73|E6pgnCc#h!Cm;o4ktDyw5E$eOUDqkoVy}u_5pDsUw^Q-GGuxJ zOH9cKcm^k;>)_Y^jRE-s)(rwOe-F8SeFGPB&yVF_s5%@qS)6KZ+J?AR^-IRBpp49G zOx2=1uwu&G?(x=ga%>ag0R@V`_b?`J$3FpC$h+9!G8&5PI&VkZxwMcJ1{gkI?G5?{ zI=K%D?8VWran|pf;GkoK6Hl9t%O-2(%F8o9_PB!rVC&$G?90MAO6~#zdvlnniSu=o zvJBQ{f+vl&uJ;2?O5^lKfz~D-PrPjn}$T}2auV#8LE7tMw-b`?8n(!#I z9tRpUX`;ej=>isz{$e3G4-XxHC114t+?K|XZ&!`>KXDS1ZFa3SjRLf-bG%yc$Oig5 zU%8Y+mKU!xf@t_5w;RC&6PcHZC#s{I?gjwGG!!r#&%B^ZkX>{J^1Vd{t|Mo4H=d2B zl5@k7iY13ly!)f3B{xWFg{d$|h$I1IeN0F|&zyuvd!;szGVC7NP=+KHq+JLSsWULu z3Ey1ylIz=_ShWPEBr3~X+>>ienRbzY^>T*;8f4k%G=s%->f>JuOQvFY=tbnV`$2ta zgJk!!-khuO&dYP`$T#*MuOhI|H)&hk{LIkV;dH)&IFs} zHDS5+Gw8k`_-5v#o4g=EYHA5gk>;X=-(G$^^rTrZ6@9phPk#cVt;q)f&AlaCn5euO zu-I{2XC)vQ0;*$@@u10f7c~bc>zhhIPOA_JA0E@sVACWp1gU`FtA{An(<=8RPw=QI z0S2S=KVOpGHH(({z5G>7k0nRl*Q)rZf(V296uXeEGf0*+9DQOuI9E+ma={ciFHPjW zXUR3gzTD~&g*E^n(gWv7n0A&~J;coI96qp%N@e)!#?q;r0kS2u@~3w-&dogu=Y5Bq z6j%}XdZ$))tBPLJe#3N4Cij-vO-2qW@aZJ+y}8O1y_gb(PF&?YB%^TM$B=VmRdp+? zB?Szmc_Nk7WJMR)O>UA&{5ght@guU9Mk$!xL>dJXDO|b;0XEva#5uzN@$?;tD1btS zeCJwcof_Kp=u4tt1N0y`c7M~eS2^ZOmtb4ruHPsWtN}Uje6BJA z(u$mXaEIEx60Wpu8F;^k5HV@xhu3R}y|GSGU&9t0)!{f#4=2JsxzLN8gxTI5hI-CLbjr;GT2Z0!3 zrl_GD{byz@*q<0uuNItgH{kk*K|)FO6v{)D&3VU|Hrq+KN*-g2#3442%=r7terxcQ zaVqpbqjr->@3im*9wu3D?Xc1$1NPt`oxgowk$pWgg7cUMQO~+-!qltVC`BM}E|EqK zfCGKKKRvKsC69ab`eN@(1B92{!9AYcpWXm;uj*#)opIHzVq@4RP; z`FGwXtxYAcN?u-AH0mmXP>$1;z|>EtABgLQy;w2FQS-YR6DN6WnA8EJYJR%6$=NGh zT5N#mCj*q-@r0XF@43Cppc zIkf$CN(H*17S60G^DcO?rFiTXx?KDSRhP-}c{m%#!degD^?c@!U~&LOV*a9wfS{#HVpG{I|9gi zJzt*g-^X5d&=+gqWd-?ev?3^0qDI%ejy1%nzUM$52e8_v3;wPtD)ed%0|s-+1msVQ zyonSm5-W3hQN1oFujgOAEJ?aqYvQ8Mx$<6cK zDBHL}>e%!Ci6QWJI!kQ^IO6oDK5h@L&^|YTufiTWG$;_`3R!#NoVPC;LphaMEFEuikdh5>XH{e(Ths9F?r5`^`=Tm)5^}@Hdih8NHg6N}@lVe0am%`-Z z(-wq>RQnU_LPO%u*i*f<>;zUv<0#d*VzYe|FlRF_57P$lz6wm2_g< z;^Vm5s?;c@bkbtjWqbSbpsC0VN3bdBXM!J&d)AKQ$in>uZTAWolkkx zsF1n(t=80wwx+)GWIT}LSp6L~u^g&c&4Sern=^y;htr{;!|#AP*OxwmG|&H>7;A9P zvYx+5S`oB-j$--W4xd?$MQ=wt=!OvLd3MsM7eTfmQy31T?`Kq>uxp-28>{0^p~W2( zw66I=9bWfT_O*9T&^V9S7JV@<-rCS(ykJsN%`X=_Mcr@&aFW|7`)rm zF7_qi7oI}%C*}=)q*s`_*2$|Qw4-$3B=keL2A&vko}>8f{wW$y)zsaZWF^%jw<|+5CIO}|D*CAY|vYq z>zQ@wpX(M;FM7|~TNPK*B9)kgSZ!Z!h`4cA)~P z9bL_Eb9Qme6Wv}km^2Oli5}e;TH~qs66kS9vx}Wy=}44uX|}SB43sn-FXz7fw}Ljj zPIRoHw8SB*91b*|u`paavA|y;JA_-VWNn;TNTb1mJ%@s_Pd@9Bh$0J|c3t0z-XGIw z9f9WAY~LXVh7H@81YC=_1zd6-M{^&#_~Pk8LO!|$gTu5)NL9m#6mw;&qceR(%|&3~ z3|Sr!A-dchv6fr75`-QDh?#Vc$B0}P&t2`!$XgpeB*%*)(~;1CwA`M{IP_G$8q{KaA^dVIs%yy>!FEz3PI z)}V2nazQ`DYFeSq?jEXCIDXn?7L6@qCI=T2C9#0<$^!W^=Ze1)`eE}fzhACF>{5$l4>vM!X<58Efgj@b0 z1B~mh1eyB^)Y_XtLi)Kk7hB&xYA7|qXEa;H_l@0=yv689$W0Q|;K-dFt6?GQ$;hw0 zmB}TnX#Y-Uvv$w+J%Xny5b88SEmXycbUtwlV@+IUNoR6JBH) zZ2V3|VZv#UIjV8X(WbAV=cfn3fc@GFls)w1j)i=gvcFj`SvE&4C0AD^@7?mgh@+7_ z#Qkt#>yxsDQhVAKCh2owSQEyu$~YKkb)jrqdWDo-cd0z`Qe&UcVt%?TqGvP_sHTl8 zWIkZq^Wn{Ozz4cp9q_A2Vo>`C#$y^Kv%W22W++H$wYw8|9BA z>OaSx`(Y>(Zf&r2rPZ{?Eo&X4v;@U8FE%oG*adSx7F94MoyK@ji)IP+&T*{#hL=;p z@^??-@KHkpr@{WpAl6RXJTv^hG`I$)Nfj{&uP=b%PpSoMtcAMhJ7hZv5n=V%TU~xX zPJuD;_|=bF(x<;er~!F_{sTtaScLry{|B)>1O$~AdJ!tm5b3aBEPG@uj8?4w|4J(=^q2%})>(1~{3=}_JyWQl^_JRV#65$&JLlr>e>-|&C0>qw=C*WC9AUeu z)Qd`P-nY;{`8c^gd@y!bh#JxS z^C~NqG%C4xNRV-tq?%A}R@GBz@~ns46p{O0&uFX}eE5|C>I-I)=7m@ESrp*PDx?6} zSU+*H*;V$iEX|F8i6?-1NZuwfwpka0vhfzveKun7(7~GW?DL~KlH5tr3-2E`>`*NP z_EcIJLrE`1eJ%hAMSSK(ly`R_QW@=SgaamSxxyRW*tI@fGDHl=sMkN-Z}hvGDVEH2 zr#2(~Ga*7m{Rubf$V`X8W-fXik77mRHmz=^d7_U|oEa@%5!Kp=)HuC9y6tmBkQ5y8 z*YL>LfWUTSvECe$aq~|ddTWsYXCFMvsGZ*n64=l`X7@z1&rsPKpMl2EMbMe>kx(g@j!oW;-H^l>w01h@i zIK*sd_i?n=!;$LSU2hBgr5l_uM$Rnumf>-hfej%aiFue~d%G3eNs8!S+WmE3vHajj zx7!rgRJ4!q00-Vz1w4IQ4&Zc2;KD1Q@CXZiB$6!X*#@H@aOVG*9dlJ^!Zkd7uR;@> znk+oB?xQc$M0&BQUhB9~IvBHOj=N)Ej4QZtH@2O3%?HLS8s3oO?)3>xH z(Os(hG=Ckc(%X*+B+OCl_3a29V0Cfs9-@G!?ZzqGfEDm&VguESl;%p$vtMD`Wij;+ z!d1i4=Z5rhVvxAo?XRmW^%4_W>{Qa6ckmbB%m?x^!e92_KEN?jP|PsOM1n9P^2`Jf zB=^QZT}@X^Kw^SJqKOL5x_#_YD2UPKkuG~e5t+pyjU~enq9RQ_C@kyjp8_M-M&JdF z0|#}^7jP&}Rs2%BbQw{Xj^BYh;K%cryGjnO?Jw(syswQ0OwH~6GC+JkiiD2I@fE}D zOa?_S@@1sh+z;>-1{USh$lRb5TJcNKtXol7NItwmdjJSDN<}eT@ZC#YPg1QU(u{)91g2IRjQnT5H(+tG`DV0=={ITf zk|0DWmo^^+ok$Yn7v%~w@YR!ih$Ttr$%Vf2&ff)t8n(uQ**PukF=%WMUE@n%3mhzY&H|dYx5mRKw`-MmZlv+2rbeaZCBK88t!HgR3OrS zr7ce4)@fqcQ~-R~J!?M`IDwpz(J{-EfFj#qWg$9^`f(iN@YmNMt~7=r`s+Zv7DLHF zLu>Z@u*7Pr!q42+NJj}5aI3R8J8CZ}7qZoN3BBx=cw2|OWf7*wF~)sW09Ar>#>*QZ zj`SzIED7c&RhT^cbIe_wXxP2n)?OoNuwUnz~$)9D}%v=E+4p zTvw)dz$&YcV;Zj{<*K2ZuwM#!qgxE4R>YAR=vD`fbT)=L_V(o-)?Pmud8!wj?Vj?G zHY|A1&^oGuJe>2e_F{gK^3NUnZBd9nV;>E^WW6zaka zsQ+Eeq{YbhCEeU_950o#p{kXeDPais+6koF^x?02?o~_#bUSQso;@;)b1RzL4$L#i zJOG%u;hu;GZViHH44IG(KDwUn4h{T|X zPAQL9N{({U#gbLXZAVtpz2U6>?uex^-tO*-zB_=q4&&-+FK3TQkUZzEO#wJyafBNc z?s)9Bk37dWmYsOLn3*aCET2wXEN0>m=uX21X3feT>%mb71;QU10e_(5gUH$gUe*UqUbF zXQqW(SI1(wRLZ%AM1dCR(7|v&IYD$1IVV!21|hq({UeTgdkxq+5}Z2Wgli<|gG%NU z966ynBLfSQYd4bWc)rkrHe~ zS~267fF}C|v4u(vSGdK~)WxuJiZjbp#7Z|SV&k9~WyG>NN?+DZg`@#*4~fG+RKNT> zdZKX|TU!6$RFI*CzGM8=Ok4aRk2e}0jX+^7G#-CF8FE6pR?f2pTQYs;P>loACTQW^ zxi=pyD-B&6_r9SE$s1X9T&?Afi)DsOj5GTATP2hj)mc~yz)zCD%jGuYR_caOu#^mJ zLI;6KM@K`AmYA=+lk3wS%plCrwpT&!D~N1XW&bh_5aL9*N{hB%U)FTCAc7r@LKlq~ zLV*Ia@npY)zs0VBbg5JDXp8nN=|vy*D$=+=H5V;@7-=l|)q_^dFuoo5ngmJI9MYP| zX(xYYP3;k45^t=zEM}{i=Pgmaxg{{G-8JZ;&l3Oi@5o%@%_n8z3J{O_;TRDEI}62X zGM&A6T1s}g)5O&G!?6`S&+AAazAG#lN${u=H?5?9oOEVtU!@Y|5OUDtK>76SJ1 z;(Us|Ut!zTsKS*}#`8pNj*_Z&^1D_!a%U3v0oqK9Ufd6w5o-p5vs2C~nC((Qrt10K z%xSdf{hBGA21G4+8LIf)O@7i7^UovdIVazSofuKxVEW#CUQDP8L|Q36U zA+nlBU{b3l^atjT^ez)?vgN}`)+N~`Y)vYO%sS{<2GG>xQl0-o5|psQ(dOxu)pW}A zT>mI6ARy2`f%^Ukm;*s%qa>p0)o7b$q_Pva3=bGH9{C?~XP>J;ocgb0SB_!!`q`5e z!@zFdoJ*c@{lRf!mZ8k<7R0myq3bR(IkNfwu=!xwkC3mSh=KcjPME9x{$8y`i|b4h zT|@=>msD|-!|8rq><%9IX9Q6ryO_slj3X;oxisVW z?H_hdg#dG1*}YDsOHfzhK*r+N1qebKm|H?;5K@D3Ku@|H^}T-R(D}|nGws7IiO74)*nXq3Ev*v9)Vkk zc+?^sZ>OQN!E*-ik;orxB@F67I6cX=vLP&ed3YhC0KCyy%tfL(G@^JKL3K(^)~6L` z&~_`97^*yjKTmN(3^$R7FV|WvU=zY@xqhHVbB$v}I+hN@GWw!teQC)0z$6WxaSo9_ z(a@_K#MMqXhXDqE#4e@Z^~^?JdBlK5QhvZTvE(xB=eQ|;s3N5Ehy^6HAuXD)ep(|6 zlXzCz1qNe-NH1aeYjYFf0=^hSR9rnvMxdpZ= zj$V{H&uXS4TH$-6g|+b+rnVFn)0|G(CUvKxDc_7U26i%$r{5+3y0`YB96Ve2SOIzw_#|CY%tY|s37t)v@R8!GVR0jRz@6aorM z;R#OLmkpZ8al%1=zX~b!q+ou=2jnZC_XFayxbBpXaG((Q_%YC0@_c`sPLfZL7&)8T zz{JB8%}pQDmBtq{1;p#KlW(*EHI7(jozk(e$L8F1Ngq(LIZ1@T?e&1& z@aWDgadrfrs)KIB`FhrV z>&;jThk4%-Gh-yz8=y1W{sxyGH(HQxQ&XkYKN4Nw-(SVlp%y-GU5_vdNvq0MJ}Z5f zxgEdjcqWD#W3hj^5%8Eg=}5pD+Oh32YV?(l)hB-Q4^j1AX9coFVungY2AAh3`2(k} z)5~d(MP{f#*&=tLzF{PhhQ734avmrN5*N`G>DRVFuXuu;&DOTA6wQUrv}dL+{Exg( zx6!reDmwj1qwpA4lzM$#Rf215a7#F3wKMp&-ex3C$s2xrK082J*$;XK18g-Xv#h8e z{25A>QMb*=x;|1K;LOFb;6)>&N^)Z&nO99uF)!U~WYw+^^)r2$b|K(2$D67XxMByO zH_|8XDfs)NnhlZZo((qNKw2k3i&AG?Oz5b%g<^)RxGJj*1I~7Q-rQTK9Yf}ai@F(T zQ}#_CpwO)2m`1MY5Il_xmmuN)xc9xej3>~ziM#iIR7Wo3`8tjrMCM(Ao0Q9+1Dy{* z^MOzu5bOu(&V9NfBoY_Om?Iox)o}jOtXVDuY5pO7wX1={J;67OB~=Bgn5roVC8vCt z9?W$Joi;wNlao;(4E&;-lRPOi85v$SSz|1@_{mZoKRq2t5b4|ly>tnSHtWq)JXzq( zka+8!IIU<_YHQc(!{2LjkF-V@L~(RhMHt8fX?bIa3?EQz2*G`V%rFfWd(`g=~w!!hoZXd$$eW7t^udHxRnx8W|b9UR6)| zo1onfGBgW}rhk2r!19wjL?0b|scjrmx79L*QG%MgF1nJWn1&31TGb!7J=gorxGyg| z@SQy+gTX`HSR$=f)!r{>REoWJR~sQVS32|?h~)Pfg%La5qj|}?zfeScYm~NsLlUxb21FT{+x8zF{#yOD11~BmLFQw1Lhl z_@PCPuAqi-egKW{>IQkE`|8?{dy#9o2AjfvJuRporH4kC6)z#!`^zx%_o`9tR{B0P z>rv^1y3Aif6uOW#H0BHg5bsrQ$9pX_F1#nPGF>lkwO9*T|D_9r1ll8|l+#-U1F2!{ zmDhR&7z$V80^Yc?k$0RJTAu)^9YUtsgJK?UdHm`j&|tlnBt3LptFabd-pU_O`6fw< zmH?&U*!^3tq0SSKsan5}Z*WS$R5#QP7Myp1A+3N{*v%tMrjqJvi^5g+}Z0!0;gp`H4oV zM|a)veF4Df@Yj`5F+)&aaI2hOWy}fMs#u&Rm}lCrPZV)yq)`{+bo1t7QGp#2jo!=g zcr=R1@Uvi?@o;05lvhA4{qAldM@+7@eFCHyN^UqE>Fz{KgCZWtThse;M0AsV`PU#wq6X3z7r?d~4tsk{hoK z;#VSSk;Z1>pFhcb^p^-P+j>t14g490QU79J<;99=BRSl!m$t?}{}O_;DfwgQI~ zmis)hPHV%MmUyJfUUdplw77Jb7Z11PC5=8*CB7novrdXQmhN!ih-mnWg~kqW7juG! z^#oxY0DR-P@x`#nPL_DAID4OhU$U{mI#sYaC9!`e;M9N5S?xlS=*pir(!Sf;ytnKQ zwl-VA|CtHCk!ONT)x90GPk|xSnfJTmr0Xv=Ic4WkNSLSoh$@AT93f23GGVD6IOHsi zuYXJ2Zn|rUL=)~!vnverzfIW!O`_)xCpj#U&A;`#z*s(nt5`iVzg5%rQad-pVw>mR zA3=341oK!cPBs70%(QY`=zy#n`$x=xq-qTVsMIAv>)4T4W#=RjprIj*vuT}b^!3C| z!io}C#_bTir<%;j3~K}I``ysnoo)KsBIGzOHU=Y)9!?KEOk5#^MBW>vvVW5dW;z^# z))wx4l?GF7%rim+XT#AnX9s8h^2xS9mC>C+*bbSM@kNpbTEy_lF!s{!^C_Rob_`&Igl7K_**_;p-jS!5nB?)lPnGb5f&$-kOpK>5KiYe>&+7m zHfdn*H7inABLEXp9$zD9{f^77s3XX&{Gz>pI#_fa6J3FikD6O--L+KvopGmov@EuI z*Ur~6{G+3WjcE^{t72zw&;?dql91x9!9z{>D9=6GNF==t2ZZezq!L_C@Mp_=+VGUe zWwBUKY|vK_lZcutykfkdt1agjSgNhWW`IzTF2MGofl^`fcgIil!`=U})t{P=#2oNq z6bX>|9iG=W_VxE){y(kKn5 zmRTG6INMpZ014`4bHzxLf56N~fhXS5INSEGZ^8kt6aqzCuJ@i+6yh@=UiT$s5DmfH zq13{QTE#1hCiZT?sk-E5_5Wj-4NMH$&@6?on5|~Lh1XQvI>$nL+n5X8z|;S(_{Jd0 zASUjHvKS1*$?$@yUSKtKFA-@Kazv1mhJ0v!exn^PZ298Bws7@2?e`)Z?2_|MyV#tCs*;$|Q&<1fa6x~H^kBkv?C4@uw}wXB{4#?qNOui0^SD+27{IUdjDLV7l7ik{;RI2_(obWw&Ozu#( z{(kGmwwYVjF11Mjlt6GU>iWVer4dw&xE_y$DO*kOrc2c+Zp#YuT!nyELH zDfk)E&!_Xte9ZQf=Vm*j=LknT$x7~^lEx6`mXWutT1}W`Hfc5o_B<5nl|hfYNhD&< z{0O0>ew3cg(1IVI;Qj^kxQrjPdIgeoY>zerC4}R1#zk zEeK#^pq^(B`UnkjtGiKWU*cn2`Fnlwv~o1IH&z<+v(vKn19DBjW!#h0en*qahn6l` zgqW#rjFj*Iz-hG#eS8PSchrk!4y-n`Fl(RVeBS1U*R`KFpBR#bB~T5ZyHTxAUPgjO z1}%mvxK1d-_du7R4;U#ECD@dXUPgHB&|c;W2)jV<#LnwmT7uWMOrHD}FQ=qZHby5^ z$el>7yK9Y8#Z_zp6e0OR$7GS?Oh>#%e+2PK#p63;CbDVKku_D;w?}X428xtDWwXkgDIWo>_+mxrCVF4oeH7-T!ev2AZh#bt8Idhy z?>Ob&cm~9;;V*s78j80S| z_w`+`+AcnA5i0o<`a!v;a?ngW>aPezQ$}iyv5&fTe&140Lo?MV?ZLFp8QHZmrrcbJ zqWyb=f=3?>musAs5A4G;HjXw>wJglu-Zqf)9dY$oK%6wg{g*i@5g)5FN0@fl$>$^% zPOVY2WVektH7Wi#mAa~douKwZ{X!KV-`qJ-v~31K%@i;!mVah|gNC9a=LOMhx!Pph z1+gb3w++l65E@JV1>%Tqq9Nk#gz_nxahgt8MNO|6vAE_dm)&%{Nzul2`s#ED+Bv?m z^(Y>f{W5eb){>LD<(QxE+#B?O%}k4(%02!XziyE=F^lP~qAB`#|8SWkEXn+kzo8&A zvGA0P7CL=OLAbiW90ou9#%qK#8Ls^&fvrMmXCfVTG5zpyz%zhlUmKd6`~XC1!?{Nl zDON2O{x+U%ofBt>Q9)P}0%Ftrw%%jcNO{v?R!5H&Vtvk^gU}+I?X*wZjV_MYQ&_Mz zS{%TW+8evtj-qv=7Fe=RvD7QXS2o-xG!)oj*+<6uPC;GG`CMk$n;FUtF0qBV%3W`3 z=_K)fJOo7TNT_BQR=DzEk`J#ub$vAlq8btrL)o;g7F!C+-4Oh-CPrM`3AO;cj`hCy ziARB`h2B4L#8`}}n6S(ASWfx=_=tZ;VF{s+%A7Q5M0GyX{HJubsTiuFca zRSbazJfJW-%#dYKZa?xD79$fJpw>~L4CM=dhudhlwT=_$=2`#Rx=a8&fkoTM8p1T! zv%q2|%%ztBP;NC{%9q?0-)5%Fr0?IkXhE>p|}2SGE`SSdGV zlZTTMu@2!E)`f(~35mLbf?`55985INou9f2YnA$2%5G&>19^pI^fDx|3|Y=EJvpwO-i}w3TADPKC1i^1s3i;yK)IJXo?W%grKuJ-xS@X9Ur#P z`L!J?ey%Cf_2Ez(w4B+$G>mk`ojaX^A;nqcKGVRZN7_f*s@iVOuPbk@Nn|ysp8a<` z5-`nHTu`593mVZH2maUTXpfxEY+1ZFhEM65rIvK0s-WE(XcL2uYQf5pL)hP zhN+2*Eud^NT6h{e74X9iXOIFn-vhAKki|Ooq*!2nk(j}7zpo)MCyOnB6~_s;zGL4= zUd|(pMgeD;z47K=j|l_xPxC|CwiUY&)R5C-jo3;XoXoqW+n;W=5|Sid!-+8rYe#~S9*kV zBqEg0M&Z7A!|AwnGY4egJi_hO(j=XH&}ppeZd}KDvZ$Dcm}pr1Ry&9W{d0?yyp_M0 zDCnMBPW`h=Qlp5kx}*4aXcu>73}dHD*=Ag$p`!d(s49xL^1=4qBx0M_DixvteFS)l z@%w8QVRfeIm@4aw7tHyVK3~uGZd6{`AHY89s~J%=?0m%klI2D*Z^SK~1d>r(m>^+1 z;5^jY>1sVU@%V_0w^;0$>fhBxU6V;)VG<*Afgl<)EsOhs%;Ekos`mfN|MA!j{{tN{ z_$%h{Bcc+TJYziOP+{W&sbfr$WppJNLBSz$he))|(`424dx{fPA{BJARrKNN6 z3@TdOi+@``nXqCcuT0S`EP)bNEQfe-fEhhdJxJ<0;89Q4DvKfuGw*Cs{ zYJ-E49Ub%ekc$0(h;t0Ax{-Egg7X{$(y5o~I8d8u zmoi1oOcvxoD|P-lBz_pt@Ouxm6+6DzJYSsN{KZ;B9wAZO;~{lo5Ltoxe{X(gTu_gt z1{sZW61W;b9EkBF%w35zdkd%bt&}-A(+LMWYxOhSk7du?K+Qf~UFwMb0XbTlOi%@Qf2aRr@(*^w3H= z8-x3a{8|_wFQAsxv@hN(+V&bwJ#s05UBUJ!DphD2mc-s1&h9)`PSp6&Gz`p zGM(8Zgvq*4QF97h#QnvT?UWNhm=T_Tz+@d5d>UR)>xlC*wAoBbkYiOQhFui(sVU0I zY|Dj!0a#Z>cE#AQc|255-e1gVxbXa+#fOEQT&^qHCP_zcKq3t{{99!HPnBFxE;$1ZqRu()RntU@glFt!`luu2`^jc4ghCa^?|2kjW zYw0JNYF97?K$ty>JP{S%3Y?8yD~bwVKOdK7rKFIJ$|-0(#56{iDc@LM3Mkr8AzSRV z;pG-JmCgGKhOjvEkh$7|YOylIdUL@_1vl)Lrmx7(z`8~-syngTJI0Yvh zHF)2rDl&E(me7Esm}kBc1Z%9P^tqS&#Fv@{x+PsOH2AlE;Z5hOF?&EZrcJ8du8FxC zk4SLWcxGG5FXwdo=>LQP<#@u(5FnA11bHa|xA7uP>+6^n2R>tc;_}e@Sg=KcTyXLb2^nJ}vdC0ZBW7e3J?yFt#ti4oG=r5W%b-dBDiwO zX|kqqK@J}I&jDgqq`C;|f5xmP+LyyKPG8b5i1w(KSN{97SanIqOp*y&f8)6~V*!S@ zO>#2CwL7Kp(qv&0tMkrrcvcCd5*@w<1a<+2*+4^i*}Y99zPv>7!jazx5uy;9K@?vX z$)~pRtKUBhWrA3=&>d>qI*WYa%q9*V0w^D?QTa98X7+x`J`VPt*b4~Shpb9NHAI-Y zNoh*WfAf3AS;vp8MIOOV+dfgGcU6Y_E7l1f{tWiq={DeK)$f*Cij3-~-$H$XkoMd{ zK+K8h>?M#?l%3G5ZgoEQQ>iWe&Lr>rh!tQ<9W#r+rr3Q~FwWD^4@6{5rd@F>E#dp4UYxq=L7Kr(_31|*uqpT>|G@gXcg*G zG;LXs^9D&W?s$QROXh5^orp8VOh_(L`Em$>pqdHx@k~ck1T4-U&4I30D?9F)h7Va` zfSW2%`M&45N$nw;^9_pkwqWM|Vw;}nYWDHBa^LoL8tSm7VV87^g!BUUGm&XlB|v!u zS0<&VR|zupec5Q+g>(v>OL=3E!%rMYQ%SeSO}?g+;XQi(E!h;ubp%7Z{Ax^a-rc(>a+Cp?>D0l9Fj7ej zJOvH_l38Adwk{l&A!Z7sa=irx#XWxBC`E617u z5sWx90q&;5Z|l|%*~X`R_hgUNxs&Nzn(!h)fQ7^S_;*sfBtm?CwqsIi9$BVj2yAa7 z5o0taOO}#wE6z7ec13lXANnYi>&ePo6zD+H?i!6=^$4#)X<7z-y27$XHct%>De6r| zIWjGkn<$NgpfgMvhQCqq=ZCgpyJ*vO47ADV9=y)I3Q-O~elkbZr^VwkXn-ks^(|Gf zKk4m-;CoF!7_xFzLpRcilGHZ~?L)fwenTP3Gtl9Ld#E0Q=+rWEk`k6Dsq1_jyNo&- zi{h6gOm@Hp&u}Ns6i7f;{GQYrEEkhdst0hWSG3G%zG#8+`vb#B0dGp`% zA4&R!c(wnkb$?{hWAkA_^B|iM{5Y810Ba$sNQeN<2JRau(b!)aTgmD8`JytgmHb-H z_H$?$Pnnnrt>AZJ^6vpjMyXkC&igx$;yMTxahzPRMxGWqq9khP0z=ha_y?Pm3Q3R7 ztl-_ngWpa+9JP^i2BCCGLDnSMLVMS7nn)piu?x$4Vo8wd@3m;Ro&3Fx zPI6{4wBG;z6P2*XR2U?|p$xF;R;T&R4T-TomLPgrj)#b*PrR!$@L2Yr zGWAGt;LC}s(IZ?+Ill@&a_-N5Ya0`5yKxp^fYzL$-ef28zeHJGyEyPp7D`3rO~mq> zHCD~fa8km&+nn<4MrPe<$0(d&{&A9ig}LizE|5l6`Abh<;%haZ4$=CB0STqm@cjpB zfB|xxMBLxYG<-kx3!NcUU0Vw{{l9x|v-u`vPTM&!z#i!HznPSk!vuJ0Fx)_z+B_)N zcr9ETW<47tx&vBUWamH~+e`E>^XyqGhw`{=diuk)j#EAU8S30ja#@sfuK7)h(yp3I zW{)}G>Gb(|zhx2kLfr7u=>$)IAT@BRIlc-d9(>kakHOPeoof+7JRnucL(3kF`U#D zs|tbTEOx|@=&Ol5g#|?qHH8tsWts_jx$|Vihn>u92T=m?Wv{&gQqs>{X;*ne7rU?H z_IH7I$DUYQZAvhRRnlEg^l$}H@{X_Bi}rpuxN?J{@5fPr{==td0Z%Z1{9hCPul%o9 zIb8qs3LMCP_d`@&B&A*He+y?UFzpD%7jQ#vQ$ul;t9_nExCogcDm)EP38LtQ^H=i( zHGn>D;Lii@XWwp-9~kOBxuyHe+?`u9vvv-YtQ@2$v=VoKaPE&@-lF}8m~jN z!h$(7g&s6|yb_F?jqz>}x{IiaN2+zmLfpCgn4<{8e_4z*_*Ipy?S}J`|ro zYz|-GN6fm7Fw8klOZw>y_CEMK6l6i##Xt3|UZ-rY;rMXRglGBes+_XSzqL#x114p~0k8Ao>t?{mh=9GO3_r`$f??_^05Ih*)n*xtK z#RC$$YFYyD`-`zhfe=(b6Ck%`z^;m~?N}`r=g-uELjPaNfdXI~Rx z=#CTEgfD(1phv}zHUl0}_8tDUK&9HPx;l3pzwWD6Xwc^JK8pGjmJgqB?Q z$Va%3@aZY$;c=f(^gVTUaLFIcjVt;XRDcuMvM(Tz+3%!Y{mg7r86b5OraYfaj*JR3 z$UO)4-Vz;SD(F7Hs*qtoJJ830JH<&n_7KG{5``aI7JII>nn)^ygjlS32ZgZdOfa9n zzjYKuoMVAuL}Fg`cY^PRef8d`1YE%8=1%J#K$NpwbAVZ6>epE|@40a@61y2;KO`o-U!Ikb3wVYS@~L2*_!mBCO;f!(9ero4`K#ed zNKRw&zdhn4d}t`{@Ubpgn)b)tTK45eI5?S#3YxXVw$O)pKyb8J`r~hK5nf9TT|1BB zsu=nYzILYk4W~9F@|e9(Gq{7o0l9`RXc6cB&{DMD%XR=V_B11@zWld=ch#LjDMg%$G!WVz*PFo$$sg-khO@5HL#2x8^GvWbN9bL@BYYR1X2DG-52`z#iTAA3d`(L77ubqsD# ztKdFs#aCEp!}{NW)gI<~kx$o2DV2-$1`HNBC#u(1b5rP-H%JrB77S$O{Q-oCrj1hd zuT$`hlXt%|2%acv!|z$*>b@=qpAFLb7`7At|6}VNgEQ-*cHI~q+qP}nwr#Ux z+qP}nNykn)wr%^_{eI`vIkk7~^>hAQM>2Tv+9QfCXes$gj!)ly#o0NyjIOHcY{X(7H^Uv#=syz$5Lzvq7%U zbezS9Xc)vr-H*$f?(o8`b=zVRk5aC;hXd*srTMoUzg+;I?#R+URdw={00SSgWOYbx z8*%ie-1l>;f&(RsQEKa`+XiLgobSI0PNhkU$~?2kZeIlD&LaR7R7;jOF1TIX%T7ne zne;KTj=HWxRclKwO83zSw8G_)@PA5;^?y)Qj{2|T|6O<;k}@Va9c!q{`{Se7_h@N$ z8rI=sh6ULevP)2fP!?0&Rps-xi;qHexEaz9UupfU1gnWCp3KmH#P2F4&L_Wr=GM9d zAcoYVV+wXSMje}Fi&$?LI6yfkNt)a5)Ch?Z3lgB%V_1h9Y9ta!qAsYbI~gA-Qibfb zwZZJQ4@r`T`95Bsdvc!@xaYSm!-|e2@j)=e;-Ir{5@t^)Ph)T?PIkyiH1Jqs==ST~ zcUiH|N&Sf1jb8JBSVlXe7-oL1jsErXvA#(91d!OEUUQyw48rFZ-u2%>c~%G<6FhPU z3s2Xo@~2eGpIwE<)n46P5(MoG#)06{J3 z&zfj2OCK7meY$Li9ubpyyVJz#aS2uJmqXUIxhnGg%$!u61Lh&VFuO*Q+{K|0*)G|u&4vB=_3R{kbwt5aF#h*(ZC%0ez?1FUZX7ya|RpAOLJ*^K%P!$#@8}X-A;sw zRqBcTYf?LKDa8j2zz}gO!4z+1xwt{5jzH)^KP1Owf2$PhYjT_Vi3OA|6s_oKiB72x z5c`_vfd}UYMA|88M;VGbeIGfAO|lxRgt_*p*TcGth*=M8C&<45|_@r~*c16a)=cg6Y> z-rrDQ;T4n>bQ$I{-BOY@6Hx{c{k4Q&mr)kWgTzJQA7NCpDUx%B$hgRzIg}%X`r@8O z6fdr5Bn=)XV^Gx8Z^jjE3}zY{tCMVDi0p4FN3|5@@;c;xW_Arz2Styx*DhN3MNmaY zdSC&h@-(dAxf5KwO9)HO_ZjUWjqLCnwM{x=7iVMV6l#WVk z=K2x~kzHJq8Jdyii_lg}R8J^ZA@?U*k~9BQF*<%pEVA_@M!fU8hcjiLGAA3{$n(=X z5u{bP;z;oY!+SZrTg9FOznx*;nw@&s6DO#&gzQ|tg6bYA*7w#76@PZbx-#X4$2_2& zaoD>TLI2%GsNcMX%@ZLWOcnw~pH^s6<97sY_g##X$5lCY__1VEw)mg@j*ARa@+ z25V{py7f3^xp7vM(xQ%Ccis~1SZeSDrHvZm_xHmkog0+(QVO}?%^bw88hO#EP#DfE z_@KTYh%!c*JGFHR#oyw0UB-G>eY2lIEKNegOUOK;ui9CZ(GO)%8L?WKx*)9I!XJ-d z+ZJ)#bH@)MUmEifPvW7Y!|XdCD=D4FryMI!ZP7D$YJ^R7fkNR=Y$GI@hKDPr?Q?njF&%(ZdBM*mn$P{0@ zAcVIvU6+L~(ZLDai0qnV!zN#s5_c5#ZtaM|U>nk|-k6Sl1Y|lS$+id#)jRaJY6g zoO-@di)||1J$)CqC3Y9NH+NA>c|xKXi?PUsDcc_oi4fgLwPuupCG$0f!F0{MAol~7 z-@7e3SlCVoB8*n=C_5P4M|0t`#3dhKRO(_gl)ua0HG(8RGUx}vmcpEo%UK(ddnns_ zy+E&N2fTUzt0zS~B@K5*f%Q`PS-rrfi@7G0zu$N+qsQu~)wSv83PV| zw{O|W9UN+NO9)W){{|-gm#zM%nSWqXXViiJZZ=Eh?BgKxmpj^CzFotJq43*HFCZ}2 zeK-yAdd;iPgeY$F4plB)H;2e0HF0|B(nNeU#+o}oc5JGbT{h2@1P*)^qxurOCERKR z*Zz$~X?yUl7p-Wdol_dz%C;&Ia_JE`@eedZ6)EBWh@jT{A>9o)gj6H@ZOj03C<#z^ z4a$Iz*s)}+_1dL32Lqh}Y|Cmr2UYlo_3zHt3+mM}3YbRP#M0<8m3or1muqHRs=I&@rRH+SVybO zdq`gRhTV34D-tm#ru%IA+9=Ucdk-kBl^Srbb-Bj9QWyc!$;%<4=$N5(rdkT&PGf7v zyIkwmCm^Pjvo!(9iM9jTDa`>Ow*P|j|9)Y>KxsLM|H1|eMA5y4`9G%!njeJocl7A- zTn(LZ?g5k>D=dzr*E*?#W`{)-(j5eb&X zz-f2lyMO;X_DA83Ao@$8Szzjucz|24YMdK3?eDB}GKF zB}&xwMz;tweGOiH6~F%k{QF-qLH?`wEC9ggbZ_EkK83Gk!}*3@@;w`BbW@VfB>|B| z(vI#(z+#8airBy1X!%Jq_m5LThxT$r9!n7bkl0RDg;XJ>x+BQwuHmIH3ViU&Sc$Y5 z27E6i_|hJ{BKZ+})~ltOMWUHYhqTfPijvjNQt#f5XQ4Gt(&TgwH4F41Q+MyRy(@y9 z5s?oEbde|8f@ZVt4HT}HuVKK^MBP$_B#7~gqYU9r!t^ryjLh;8Hnc4m+c-x{Go%hXpsc8-oXUzVA-MVuKC@;M8tieZg6=>{CN zqn=i8y!_NV{D-=R(kWmNYISfZjsJUxZ5FLhP+f4Q)69vyGCk2f1Ur-tJ zhQwMUDZlN1v+O}yxh5u-<(l!faTbKb;0K_}eQ*oZ$iL`Y&^vpxtti@Og4aGhg=HJ&F~K9^;U?r} zuia=fxR{fHHe9rAD+m-bY;7=@9STL(?yG_~TvI*rgG#~rdtgI~??47_?wZzX2Y$up zIkX6hZ0>d}8T^;oD{BVD|28cLJ}Cf}MSsQr5I_?2vkrSIVYh2&Q{xYP%R!TkdnoU! zqcnF$Ee~p=zabYR>Jlm(HZdmCK{o!j)?T9jJ#13lS-L_vg%AR zYm6E06}Zn3n;1R&10 z!sHkKWp!BXI$Lfxt#hs8k40mCYaD@xs(aAwL97UbjaDAmqKZ}aVD$&YXdQ2-qVF8( zR2$O4yEATj?kLPdcVZ0M}+{s&a6)_(a+&qjIbF> zJ1Ps0;EA-ahOqt{u+^q2#QF!q?uUlEn3vUyjCc}|+SRG)igN6lSvNf2vREuBb5EGj z)4lT+;zQn~Mb&irZy;Nl5+vh|UxfeL@i60ysAIB2AKnAWJ_fA|3%dGMMl1XAH8d5M*Mqa*|}(-v9gub&7^v@MVlz+^7wK&JFqTQXej~) zjYY-FnaHQu73QpZ24YmAM`*hK{%Hv9D3jAyN*TH)k5{PEap;(XA{dH-6+#I!iV8fy#!|4kb@HwP z;hc_hF@_uw)$vgUnq?$d(fBc))Bo&izYPUYb`VG&00@H9dMvE3n-@9cIlnfMwCF)t z%n7v3yZ3?*&a;eA?t;XY(}?H?Ts8~;Ty(-mu0_9p=Dvr&$XZEa42FuZ%*)5ysGDrx zbvGyZnoT4atmTm{HQ^Qt{QTZ*dvZA49cMc!AG*aaV?ZCJjk1jnQn(7Lu(abBIh0xH z<1{I?YI(h#{A46gn%YD~HTT%g02OLW5gQTZ=_45D zdAy}ahtp20&z&=SI7 zeYz0Bdc`dslMHJsEQ$n1Q9QC)5%SvB*Hy(QVYHxXP*EK{ti}_EhcLFh63jzzzMyH8 z>7oV^^{UEjZ0@Pb9GyL)xukAF>v#kd;4VVp2#Hf8(2nDUZGM!o>VCy8d?6J^YNkR|O~8KD4yI<$T5Bnj1{bx0>?+sf-NRF|B z9kuD$$DWB80VI)0qR|Y6XatlG>Lys`57A)@^#?b< zhcJW^2|xf6QDf zyYSoDRUMk>#-6|Vs-O;%QGQt~p+9foQ!i2#84q!yFOW;+%3D^y!(HVb5zo6Kx9PDL zu$c~kd-DmcVC1ZwvA=Ea`I<^CiZ)HrpWPaYORd_?Zy^@5uF7l|GV4RAkI710&R^7x zo@HMCa1}Drp+P3Id+dno`l$fh$iI;!uRj29T5Z;TdSVW$d)CGHi zRb5q;30arq++%4Lm-!Asuj;KzPfGCi9cOz@??20QhLPXq3a?L}n*u+(tEe7>aVGs8 zC_K&jI*KTxLTCTPFg*5Nb4CKc?XZ-;acbe#l<#nKIzofNEQfD(=;YBZdMQ*m!9$_t zKT-Uo#H#xMisGPm625W_d`fh403(;DlSdqjUF=u}YC!v_BS z&r9~0hvEBzt{i;i>g2nv}8r2^j&ml@Y*Qu?*5#IEmnYjJ4Aqo6ol%@Or zlfi~2!e>lswfBy;6&W3TBEYZSqNFCvn@ua9-P?7x=;_o(LfnLv5#;9#avsLi*nira zr1nDIuY<{1E5iGOED}PtywE8VmTIa{MJ{BpQX|}xE=wEI?~FDc2fBA|Z8^Bcg5B!p z>wAJR3AQ0%fyi2ttqt2N%rYF7BBNf!>zZ4d$tIPN;P%>Y3+YI&N|<&)EXwRK6f*vd zGa*5CPWiM6dd}g3rkerI3o+JBqF$%D30ZsLu2Dfju0NDkJ5v4urPo}|6pjKi%eVkgX3v*OvBJ9INBcigiJ;@8FQCjcis*Otco_!GgWQCSw+HQZdbu zOHw1UHHT}CWXul+zvEZI>iVT=Vtf9~mCBF^|BNdZ!<94YTbDc1to=y1eXL9-3f65K z)|R9$8>;)xwky_nJ@_f2a$ z3A)8pP~k(zoZI(}2@{?k5nPTHOw>jFsYFDq(>k|ipydDVx-@tA#Vodo=Mi>g>4(&t zXE|nZmPaO)otj)fYNQR5l0X+H63FK;Jz_~4XIBM!d-tQ?ak_y`p3qP8@YS-EYEmA=d zPfW4)EeXW~5~34DFKE|cKzDzYx1a7KP+U6g7UvIrmu63X*MmcP?zOA+yeabrw4N{` zSpxC2g(}2r=)XqIW4bnOX8-Sk{vDT&5H=tH7MxFgQaRRz5w237)(=Hfw zq!WTN{q--M?R>TagqL|2ti^0Cmn^*gf};W#$^P7i*7Cf(+Vg%Rgp?lHhk{n1e$O6T zNZC7DFydX-3dQS2CJ6z?i?P(A?;}e@ZZm(r;*d88Dl-cGeFecy{xa^cUCQN0YG%8w zV*lwe`O3MKt!@(JlySQzi==QaLVVt}Ozz46Q(U_cyuAvpZS-B3TAq`k0oimj?G@}w z2>2G*f!S#-;o9ieLkdS(1 z4UHS}9JyBH9Bo*ZS&QizmK$Ul&9%OI%Anpm! zYJq~H0bFK5m{%J~o|!kDcRkT~IQ6dN;Bd%FV8>VU*uW1Kg8J5*_UJ?vLf6~BHX4qW z&XP3+M_crj-_`jRE1ZvKSCv+Im02j8*0y4C=jG&J@Y?4@E*MM%Z@0$g59;uz0WD{c zF~czF>@I|lkLK)Z`$1bK^;+oX@S*GSG3z`3y8WR`A!(a3gg6)7;pjRolOTI_hT_8e zB2KaLZ!9(YDC=GTVxJM(%3FOr01{60*JU2PQQ+Jy{pJ*~6XK*N#|6qslZdOjqjkv2 zmI!FcT@2V*#+{T}aWV__`2MA0ZB`FVJnQhcMNjp#?(QHYW^O@w)c=iB@PWgm6lcXSdCJe(E=k=yW^K4jaF@(eLPGa z$GD)-SlzAIo>FbbXywWTcoco)%`9d60?=61s3Z1yD9l*nbH?O}s61C*^qdPUiE9+9 zrNJ3vk@NST^MB1J)ZbU*3palelSwhzNaVgDX!~6Lu7V{1OUF0WMr|ZRF9FN?;1ex_ zsHhFMqL2Rq>VN9z%)zpwHMLC8o9>B7HIe=C>3)AxxAp<*wYc&%xGBZb=iv}V-5p?> zX5;Ex>rQ7aoyOyYu{Y7cEx0Huo^yvUis{pi0TK707Ppy{F{$%QXg}SeCI~dN9Rc{a zDzqjWopun?u+PUll^GE$B~(f}mehvIXz)#*arP!s-Us1y24}NO2-1((Qc`X1(iE-) zhZ;^DLd`YEqDBWapUKpJ;(k;3oNGRi>SNv{4i)d21#uwCT)by8E=Bl=6!3A3E%^q% z{*Dk(=?~oLS`!GxTu#7wjVD>BnR7>>dhOM$AccjRnZ zy&J}B0Pm4?3f!#9RMWd&!Ssdhz^zg61~wG`rGV@9=`HX+kM{l%Ije?RhB6DzTVi<< zgk4=yWW8-OwY`z`{3z)$4d0jG&kYCkIjV7w3Hlx3`C(x8i{WYVS6qH~0>FJVa9&E1 zHR*$X6~D(~qsADww6QPGn%Qj2Q9S2+&Jec}Tw>qaxTK(q6xXwLf10DJUfwTvHF@Hm z$N}Z&x)3Ve3)4lvSl<>c8m0+d=4%A#8>w$`0%`e@i9W!gEI5Hjpp2j2E}^b)W&d=;Ouj{+b!-~Icygj#EC z42H`wkZ^4QR8))BFUhGH)q1G$1+$CCuUFOEYFRuYDC05JmOc;gVRp~!DuE!O-G#M? zt!p$8k$Et9?De;bLjoscS?zp0mlhu%#$sjzeF(O#xNiVIrplY3pFvVncu!?co+in! z&n6K4IptgoX9E5qzM?yE){d!EV;qw9<@AI&zCI(`f(Wjej37=55sr-O^3Q-wHKgel zTOkz*E0>CY{!%TK_XAWynQ8aF?~x$bjluFF3Uo*q_$p*;q0wRMfB7^d>Z+uDm3O4k zIz=8_lt+_<_|U#U+sf7qLzDj+HzQV-o5ujI&%5rVyL&P6i4F;El}ya$F9tf)-ISIy zivHrPNrW?jlxW-lmm$PhcUrp#rV66hHA_iVMaee{Nh_m#JBx4Ecvh7()<+e_sgd*! ze%e$So!dXMgicJvszt7E^NK?KAzN@pGk>UIwqE+0<72UJ`PP!L1VHC_q)x zR3DQntSkaFk$CHV$fhv)YZ8ota+smOeh{jQgjaL6q{=&{vDnUEEa(b~ zz&m3CP@<|c7N+yslc;jn-7zFzUVtuNLb<_66sHEkA}EG(Op!}<%Jbtp@#?ycX02Zb z&}b04gt~6ta8ZsKnRhG3yu~2F5h}8p)9E^_xVDJW4lBWQFyR&*Ew&i7L9y%%yCxz?!Y_eD%pLo!mAXS@X*JL~IPE zGn&?Dn7kwpc7wT0*WMiw`VoljOnImhsIjvYRs6^-ffjG+L}( z{?;D)&`kOf;pn5Dk$UL+@%E4Pmw=;$^(ZW>5`^kh7D@FQzisRF#s zLY^@#vwcP^>7Y%u4E;rUv^1!RqB4^6+bW5sQLP|#maACYaHZX8Z->vZNW~4x>=j&t z7(H)w`6x?^DlM0dL-SiMQ?r#}YxU2)q+>FHlvG3s0oFC0>94^RgwQ;i)Pxlvokm&X zyo3(hjmX{F(e}N$lxcJq{19`rQnj2h1~!p)bEpp*g><3aFS^6+0z2vNLiuXZ##aEd z_sfxKx7fi8 z&Vu8&ikZM3S_M60ai9B&gzohC6r&O2efjZeIpP)k3k$p3dqpZ47g{M# zqN7gTy6+wCVOboB{+xE>YDaNoa0s46?5&~zFHn-%zXUoyl)dV2=0GN~Ssfx6kTLWM ztc`@2I^q=(Q4|#M20fezUE!fwN_}HPV$|Dw`nBvMhrKCuYUG=FE%`5VhWHR=eDOS| zafWVCswX|+l49Eax3W;7Ylwy?4*+r~xiNoSbfVE#uiz12O2wH6K7%%Ju6Q{^^K;$U z5qb_HcpO{r76W1NNNg`!+9F^@+=$blkXIs<!BNIL;qRAkd!`h9i9Mv+!$yT; z2tB>KfmC@nmYBM-uH1u)mIve>L)BnoGE3=0VNApegV}2W26$X%RKhrcs@vABElZC` z1T|cq8_m$!2I+|AwbCqi?{>}3z*lbGuV)?7OH$o0CUwRfa$*cLn6JMYEgp6FGZ3}Z8`UCSBD=cteWO63J5Yh~%bW_Ds7d%3SJ-wRKKVS}MNi9e%dNjx`-`KjKvCLZcXf+@A};tjjxkfQ=A4i?rs@ixIvIlAQjS z8HAp7oZIlXB4}5favar-?_2oe^yUGT)RH=E9W>~lnO)!o02m$9qlV6m7~-?y&|eCw1Z{O#90#g z(!jcfuy7U3&Qf1fYD!)NdOTEb(>7rxL&4W;5|{krreBs28n-(0ArM@H!7;1jT#Wz? z)b+9^3Fc8{N_p$;^Ue-Ug~6-02Up!k|j2q2~_FqzcnwjB+#2RhZXXmRF3d zZZ!SZfOWj$G|{int1+kL29gHH$s7PB3~CK<+};Yyi@5Yz#mZ$H=dXMd{!A}qH}$?U zt-?6z_O6oSOQ6>ANcLA;v{bhDsR8{BA^&|r=l#oQUPnpvw5j;|)2T;jkI%dn3BULg zz>Y!i2QL+iZV-b*YiV)Fg+tx!7ld$r!L(i3d=E0C8mhjxTihqF=9y!rY6+_-8DXi9D;um^Hq-l*{g|)7xcZQSx%aM!)N)M;kXk`c-Z3#g z>GQVbY;alC*Ye}5%c9&4@)OQfLD-rOU$!Syp1BH+K)RBT^l!Ij z`U@o26~M4mv6Pz+_t9#RpxLmY7D{P@(^n@r?@_*Wv<+!q68**)Nr(DsvO8!}>={+QT zgs!@M99Qsvdx%P>+0RiFzAzKeW>lm_B4#u4eli_nZbH??f}wjHKs4&5sreV*oaGD3 zN1maV%jqT?KWk!bXQOJGFM0I;@|&hdz}90{jR#ZBSKc`&9M=| zIRCkMcc&P?$i?AoIh)g?TCg{#bt~g76}JbM0eLaUfoh2VyT?@u>W1gJQBlv1zUA{b z;`fXPy8E?0@`=AXCXrorjGX!&Gu_>G?~Z{PUGS&%7a*VeeaO)ricf@4iJI%U=KNfU z7ir&tz1(~!qsjNuMq(|AD8@6>nga%Hf2K|FC|@s5e(!5DHMtTx+ZSS9dwpGhx034c zD?#(_wGqU267Rrivk;8E9TJl$K;D$xGIO1jZyARTg!DXMwS^7m=6ni?3kgmNUT96v z@Hyn`BoB;Z8znAPSKIKkrtsg2VaD}%yX!b}GYhWr%yop}^w;kb(sZ@vSQ4Qu7BUb% zPHPr#S0l`j(2SdX!j>L{#T{FCzkaP#Ud}T)kb01&4mKwcT^;Tu(O$<$pkK#$Bqa{I zA<|LAJv3q=uGvqk=h^n(O{Nobn^1+n!RYI-8h-aTaKH0nuwab=n+|CWQ=9)r_k==- zX$Ph3E__w|qqOuOv2sxcz-(>j-$Kt0vMt&FJ*bX(s@FHjWEs&^ST%Vg+7-nlcWhq& zwG~y~p^@o<=6AbyaJ}uBw)DvEYSr8s={j$?)QAC9Yw<-Zkj^PGuPZl?WXF;egEVBer!Sb5{!H}nofp0xFUSI4T+{D+le!SV`2C4amev#- z`$eB}Z2dArl4qyNM01F!xHc1s`+0p(Hr_mIOT{^6tO#?qxckqx!`j$?iT+q>^BN~& z8uMcOX6w9}l;9H|k{;hw*%A05&WfXkLaKGP)6k=BXGxX23D;*KG`)Fx`PZ?;0F*48^o%7egR}6h&iJhGCg+V z<(pv>-GiR`^jWAVXlyBj;e|0w5My7e|^G&GkguS z(DmaS9W?7Gv?(*rI9bU;7v;(o^jbsL9_vmV*ujRqtK-O^d*(lu12iiUNsozc%$#>8 z6(Ngy*+c}{G7nDAIUzGsV$%UK(PO6K>1UDP?q;4DqmE9eGr$4EKtmd#n}VD4yY#`< z3g3ANSX!vG)^UH}tO>6Eq7Z2!aO!m<_99$SKEu{={wl&nC-iIt*H{B9=1H6F=TkpF zy*LriuKo9*gg4Htf@WTtz5!I$ba_Du%H}$ObGte};Q&v)^fme`dR{<*SY{$S{kYRb zyqg2cA<1)yZBmA2>&J(xmz!%l48N^IT-4)C@Yu`v2_~tvLdTpk+vXG9#&`|9f#i2HuO8bD_$HXr6yc)TGS%kr&cUUQKxJPZ=^(8D`cqjn@y)^*bmhkyN_)5 zVzu|e-b?i82_Poi9-41}KfrMmOFwneNeyxHvYwdLj+9DG_tZl`ejTO4TPk?16Zn$7 zs=$VG7Dk`CliZ-~C(G(~^6NsUfE-C2>o7Ya#AsF*&oPD6oZht5lwxkEV&GP!A*oecU@uUd#4x)?|2lJ#buJ8Kx6b z*$KO4`4Rl#K{H_&R5qmI26kdgY*Ohp6%YEI0Tnjo1c4Waa<8sPO;Pc;~Q~?wH z3Bh7L-F_8~t?a379AzolEbGM7&0Q)0y4u7&1gz#=4zV(qzSk8mH_ubR_GG3Jo$N&Absl&0>9 zGA$3AiY|x_@(Mfec_t3jmF~D_MTq~je{e=cgF48h%s3`lIlPAy-$3JfY6E;vxvik^ zA}!(q0V5o>LyDV6`=GYD2eHmWv&lP0Mgb3qQDua_)EtPxZRE3{BQ=jR)Kr#$*^y#a z@GUSv`sDX$qDT|w-wEv8=u-9gK^T?wIF?mwgIpg?uG}B&!;{#-cPM1$JRUS`$k7WE zOf-k4Jm2gB7LAQ9Pi@xQ8ym<~yv5(iL7KuwQAsj69VR2v30p$MMt6<(m zxSjf8Me`jKZ{AyJwrdE|7nXyV>eMKroyguXMe>w1t~n8x_#G=pcY@`;$pK(|mU~&T zF{arZeh>akbDO-vkvN2}!o<#r+()4rDn~iE^e7v1+zPFen~O{^oC@~@u7>BgNNE}{ z1p<1HS@KWp5AgcU>WVQ-kmGjPrQd1e*CtrtE4zGwT(4Ka))`c}k9L>br$>Rh96aBZ zRG4Q*lCtJ}`{6HUJsWk&@L@CV4>Q%joZV(Jhj;_MXiD!yNys!(lNNFEc**8~-Fo(K zWX%odE9T1sap>&VedHZRVScY>3zLpOhN*#sFg%7NoS(hDYusjb{Ih^u(@58Ga-qa} zIjnH7e&Uh(19iNbf;wTHC?ocn9VNTTkVb65MFPvp=SPc37p?e{r2(^S>j!UTh*xN2G2%g*r+}g#b z8|1RdVb~N2}w%flH*)&-p2P&ahTp0Fr7>wd6TQ0ftjw zVMJT+UcCt-aZ-YOVrGLGkR7~u;C(T=JmrbJjisu!w%`&CyYbc9AOP{VjEg1zH)E85 zW3dOZUst=|7!;gU#ZP;dIum|s#z22i@0W)7Ag%YbBdX!!HvwITFVp%-4vH@eiM& z*dAF$A}WJ;o4hyjEq>o!vlURyN~Orwi1;(ZpjwlF)d}~h-rURvilJ_HIvk_qE8s41ttlhX@2zA+&AnS=cCJpsp=??KirR4M86Ah|%xBEPrRWVP_r zl|zJ1KFLR!B`EXszx`6*&cTqUSbQt5r$~W(@mrRzC!77(SyP;xJH|U-;br>uUg9(w zZx!K@e)@RHoNnH;SZLgvu>YFdoH8_)oq9x!478N88xY~MlnS=1>0E_gRmWsrKZ3BJ0>NIx5?d9Gj(67V-e8|YS!;xGku>L)Gn#j1a`ly>Q zZw&1iq7z}F`A3wMH*A-Y3j<3JpPUN6aeh*Iu-vE!I7*%(7TK|X_Oi#Q*zf}L)nmB7MR51+6=YQ})q=}x@a~RK4Szf*q(yQuF51OvL zB+`ctY#DE+twasib(HZq-Vb_xWKRtq@qWHlDv|zVX3OaAMZ80;c{=doOY;|;76bLS z8?-x!r|0j;LV6fQVb>2u?a?4WKK!^{RZ~FYAuU74%;ss=ua@5}C%PsjwAa*nTg`4! zb;_nmXmyL*|2CV@Xwq7+ti_Tmkg)79^k=_aG*itH6u-Pp#rC9Fl+|BhojW~}l0mgU zn=L_mL3ZbEsWy*EmG2F4b8b4?x^{>4cF@>T{~kNcZ^~Y*xw7?S9t+!%^XyW#Ukg%(u$*VRknuf6Y zy4G<0o{XWFPDJOQReEH*gQ7i0L>0Zqeuo|D8EFXRf+@yTc3Dxto%bL#^}Q7fTThwp zjPQ1sD`5-`vd9%o46&ODFB70-@&1=80M3D!Sap(_x4xSDn`9nIO8gF(+!M+`$!4 zRi{dp%?ZB*$={yl9nOmWW!~+)kEc2Oj>h1yyi+5Y*eoVg#sAXSs)Uj@15T_V!MX?S z{zWSe8zKYkU8bJZB1;XopO?4Tl-fS~ZxW7ygkc)6(V&I+T-{&LC9V0EZ7~?mnQpXb zYRcVosdLZ$zN#5)W#=bky#R= zOE$yd`7KBpn^u-?@bU;;*uMM(TALZ;O8epuj+!NwsJo^$HX<*e>`YL zG1xaU(1iOuR++M3U`-Cc8fd|OVaN932gY!n&sbatg@8BIXaeMGJ#WN4KmaRX69Q_3 z4kY$8u3T-YyOv|f0vINp{yJnVrxWyf0DTok{CI;s=2eQ*bG53xGuo^7(zoYf)uG{Q zndJzENr(I|q$ELh)o%yvbr}Vl~R>pBukJGNuNn)?%4=Zc2b5_WGQXvoTMb9 zkIL*rB@zm$3E`ImQlXx}Q8LP}cj2E=SHTQI2wPz_L|~pm9n96Brfj>v8^Ka1(NW6w zAFQE)w~^jYVjkxzoc}C9Nb0t=Y=m(68WZcHk*KMJRqp?x;5yJPkgs{PK|J0L5Es=JmmydesBqFwH75pyfyU~hf*#y5I3d;~jx_#S z47W-~r$-B=zirC(<9%a#>X1nmggiaKT7lbKI(~lv@8`{p7Pf^iCDOv8UEgc483ad;Tv6+nqe@s+@nj`P7q#3dRoJ|TC- z^rXXO0mrfWjhCFE=q*-f@`a*fW?gdKiBcMg(8b#^e06YG`~Z%YN}HM7Ro}YggC3ZF z;Wl>>pW3SW^=X|IGx7}VTHr}+5;nRQ&eI#Q-U71cgd>y7=*F%r(kD0veSWH$8vq(@ zCpDZrO5PG)$nHPCbSD35>kaieCbtxhVS&VJZVWL;?s(Lk&wIb$K6g%M{X27lGL4BL z)D{NnPg7$bje?G8uPH30&KbTsP;FVd71otiSa+d8g+=zP;HrCSMF^RAqw6DDd*>S_ z#B5Su>qzx0u44HVXf!nXcqTg}^nziecjPB*Y9J&4D4%ao_TF~mq`Pu&K5mN-0JdQ9 zJKHW5c7T?^5N<$$t(d&bxA^woOXQLoi)g2dd#nukHy4Lz05q zk8R5GfXH&zAPU0T`emdRsC@yO8ooz!Jg6Oq(xy5n1^lhaYp&&J{ygX0pB?klIDXW= z3VLK}e7;3Df}Q3`=&3^RjO8M1-`-p7;CZ0-dySXt!JOk#7oC}{W-=m7j>hx*g~{s? zP@NhwN(JX^n(jrT(WlDq{@B?MaQJ5RB?WgI;&DSFx3R_pt=KEveA&Oz&lF`3m&Bp2W$1j z^X86!PrL6qGoP>Y&94&wnL9UABc}k8(NbYD$?aP#A6S9TO~)>s&#B6$M7JKT6zUpxBuSdWnA*l0%M zx9&^SmunbSKUg1O*If~|v1-$^tI6%4mol@hVcBIHIYuW}>p%uzln{KZh?Pz57w@{q z@$}rTc$}d3{{lNe#J?&;kqqirniJ3Da{?xSUQX&oq5t2}ygvx0q)*T%$(954ii1o) z{8io33p7|0llxb=Y{9z|b3>MHLA%er0qOxLeR}tMrhU=KDT(|S3DnWvd$r1etE4j? zdqS{K`Dz_w{+WvJaP5fuE-<7x!gyC0vbaUt-A##7E4I77=d;QRTe2_q1jzStL+XYkg%n zU4YXL6u7!dyX9k_ipNleJ%4%DSvK(w-TA%NnNm?YHr2U;OxBRP_Y0Y-uFiT|8Jyi% zAh|w)TFeR`Cf9FUnn9lLMH+zd%JB1lOiE$3XFfO@m2Ogi4ZeWg!pL=JRmpobhp|gtB zYDH#6%=g)H`PAPlzmiL?@Dnttv3AbUe`(tQ_HdUmGZPdyP}Xc39wqm{cgA{>NGc$T z0vvgpW_K*+1UEg z4a-eW6$w_tGl@Ak1X!rU*Xu#X$di(zn1!I^v{uA&>J9{kVjVLp^T(BK90Ol{z52O7 zEyY0!=2YA4pW*h;)e&Uy%>5U_YQFikAmz&Ov=y5B$)VVB%5=RH)xC_8&eJEy4CxUi zB^IT_Ep+U3yUyX*-69l&=}&8ah?ZxdQOn-gy9v$YBYoj#Ut=%Go{n2SzbRKXsh`M@ znslK+wen&&L^*0q%>2-GW!8}WkaX7M1+$H&Q$EWE1BwuJl1@^j`3c{fAl#Tc}dGv@wyCn#hO9y?_c!8cjRyvL1u^Eyqj3NwSZ7=FUq?ce?<1%!6zoD~ygP>7=1DwH!7C5nUMSVMh0oH|e4NGd%m zy4`P8HOg{l_d-MjiJ7pd?Z(2}kn#TM_qA~y*1$LwQxq)mo0k4Cb$pzfJaA6~VdF4i ziOneG`A5%`)ruZP5NK-0X2aUNcdlvc#1rjE7>3-u$N=OsNRj1B$L|;VEFM3X=HOFS zUf^8`MAA?f>>9=>e)=E2U5nG>aVDI63cozyO)xqZ2TF>-*8j)U zpgKS5&>l@-hhk(O_O0{;4uC#pVr;9r63#Adn14-^^<1ohlsDnsb52Ubr31LWJtSES zIi}5IrnU)9R8<6P!J@9Ru5-j>5!=rYcItuj z^dXH0RdRpOrAs2*8^8xQm(NEy(a!I-4ZtLbPvoe#w0-cyHR|~moHT;jd(j=xptmRibc4+_ zqM7Jp_K@$(`xtQ4>-mL*T|JL?Z~@M*grF!hJ|JB3jl__fR;zG*hQoQ#6_~irV#V5&&XK)t#WRT7Jl4*@md=pS}k}#r)c$KjH+g&?K??8C@&-#WB zu3n%h`*Oma@+mPUNyfe`JCPH^^ek%|H>j08Xz#<^i4Q!#8SEI$8tS#m$p-O?E(QJ# zs`(~cKNL?f+fFqBi-j3k@P2&QG}A6$r-;Z2nREek&c?LBQ%0myAF7l?cxN$4?|^aM z7r{o2tX?gXd{!nfyUUZ>a=MBk{pVM{$64=kNVJD}p`v&#)+^nLvMCCd|2~Mqmk!Z@09t{!8cSj?m<-e-YNO$DHbIz^K}D%^@q8DsZsv zop|x3=KAt3c~kxBW&bA5pfB)R+FQTq8RV(6DX{%z{B;q7gOI<9DZq8vp68V|j#KG@ zi_ujG63KY6uv*$v?oSjX%0jD)pNQnRe_q4#@5j^NLnFuUEO7B>bn!_hZEhrgFG~Yi z*EpZ~njol}V0rw*9?UO+-Nt$osBhG7bVAkJ8({l~UcN>X>Af-2i1+{>f>gx3;CWAx zUm^Ql#|nJV@^-`Mq(|p+=*956I=iWH&Fb*Sry8bqS@z^d&BLX)$gEaF7TBc@XwyR)#qKdJs+{L6Olz4I9=HwN*!> zbYoSQ;5RU!CaoQ;YF}wHv<+#AbN0WQ9pV}j@{t`G_K5qh!vd}0?$yQmlYL#hYA;*X zChv}&RQUK&R%l#eT}$XB9BJ{j6Xff$^_#^UMkJb$PcFF2naoFB5 zWIi?Al24<4?)$~P?&8Eitz8YNyjO5nobI)Gp*Ydw8lp5gZH6~h^MCPYea>3hn^1D= z^ZN?t7?#1lZZa^zpeUHmH@}~TZoC(OxOS%05YVVQDVC3EbV<$kiYNgNgj(7MfvLKl zqQ2+b*))Qm-FQ!4C2Gi+^Sj`QvhNFC-PT6g+gmTAD^B=yxE|GY9Bv?KxDuz+ehLhfFK%rO-h*H{zzwRj9=ovkQJze}W`RHYsMt^q% z-vf+e`+Yx@qvbqcuGVO&wDS@AUG^5J>Q%!v-1%B7}`E zq8_(-x`X|F;vUT(zV<;3{0Ki|-uPVke1A-coB4ZWvqzVNxcy0{r=QVuglq+}lbV0} zd?DDE!~-(cE3(8XaFek^Nr_~GK8=R6=OD_3-Vi3}H$u+t|Al@13SjzT6rfkZi0O8l0d2t-X`3v!SlW=xOafvBQF@k87E=O?+g1tB-bn7x0-^A+z)(h z25!GsVraSB$|1}Xa(1yREIj_=?9v&(QR0t>e(u5jT)ZV_X_3eVM4xox;vW}ck=A`- zPeQ6bOl+V=4B<)@s0KCbt)%U{6RBp1n<}wx4za?trr>G-n6W|2u`JpARjjxVLUg+7mU@Wk{QZIoEi{CQ;Ar*0TYc!W zu99`&9i1=q!xUeb{kCgWcs5L2w_>X{a|UDyCenMhtIzLKxE4FP)91?gFC?EKXMtl^$ z#Cc&x2NaTjE17(pOabY`RXC1cGuyumbtfCqS9joX)YKEyhcZQZv#Mpoo6k|>g7z_* zOOAXl>i|m0f|u9{Q(mP5_t!Cg&KW#*_ysY&N1wVaPd*Kjugt9Z@9ONgk&bgb+DQf_6=Vzw(wk*^xVvwWB0_n4Q*cG@3V2GCebBFj$=4e zUkgzu-{&2KlwDzh#g5CkvAas-fy53vILe%qZvlSy?Qq}OSs6x&7B|xJ_)By2Q!B1F z)LCiXu*5xinwH1L=mY_uK`KGDShavo$vNG+2VhIK6ICO>`)eZ>T(ev!*W?pOU@=!; z@_-TUbU5lw3H*aaE%SGEMFOj{Z39-*4!Rm_*W77)k=T3PiC94NaxZ>vugm!&iTQ36 zDTV42q_j%7^5j+LmkjVCZNe!ScmMu=>C@%zh2ET9MNAm6R3|m=iD#h5ZUi4}^Zt79 zB+S>lZs|Z@^389C$YoGxOs{r=w z!PV7gRABR6MNU~R^>+3(P6?>Ddzvt{~z zEk9R{)C7eW zW^y@53*uG*lbM2^M8Rzh0hBK9X?-19F4xdb!pB6$_7HU9?^-FNt1T~PCO${kGc+xR zB`sn9s{Q-lwt5u7ejya|>W?@q@(eqtkWL7rJ#c5IwmQiW?<>BjKA8pzF0wKY?E*^T z0PW_SaHx@n!3z|Pl6;3U|4pHZd`zm6>1Z7z!=upp2jA&z<_c@&9`?EtKcii@5KrkE z^bjJ0cp}u!K<@yN@~@Bp3F|_AynJyWaY&wrm!0%WU@~$1Bsub(>NbWRtop*s3oW#k z$ee@3!GpRjFZ}Z&#zmnz$fZv&{Zp195!uubyZt#mMsciHzQKj*NZPusJO? zdBM@#YsLG^XnyOvv{^`_0R7lxJTztuF=e;``7uQ%*_fUoG`PJ6uj-SU+y$m(Iqoly zl=}qCA(oB4aaobLRXi033>!^s?sk|e;IgxPav_pQ73IE8dMBHGqDu7}Bza$vNUvCT zgsK<3tMq8Ylu3|H`6zGbpNSic59dCX%ip)mLKHN$3dVRMM7D=F)9M2V2uQB=kNiDr zB+{~rL)`p*4^fn$O+7orzLZDkgC^}t$oT>hJ6U^H4j|(Km!OAC1VB?A4hP$Jw0NYL z4|$1l>-lG^(Vhn8n~`t}a<4(?fi}|uT3+%IEhW4teQ7z)c&?S|H1u{drFgF_z%Wtv z10pwh#xWuBmGpi0>yz66WaFUMvSV2ja+#7-+T9^-S`=9|RWR^toB+yo52y!T;@pCG z+k$wE?P5J9?E8FIS!AD;{$>e))o-Bvu<)4RRCfu9gFbHL7(dqR=_}bg`>bu7xw?Mk z;CKVuA)wsGo$5WcZ?lTxsrAt$2V#N?HXn;^F(;TL2pjC6^zA zfn#XrhqLT2FDHZQi6cj+UiKn^2*%A7R|bL!p&adyyX325Lon35gZW(z(7_GbLml=r zy*~*8gg=^08&rhJD$**Q$Q5x2cUX{Nr*^m;c6aC#$#FZ={CuW@O+K9|HPAV2754kS z%WBq|(y!{7S{`x!0u6^0d-lxOd8kuiXflpxXb?Z14yIANr4qSDp7SY#XA=n(FH?l< zFE_IBbgmLt+^4l6R1y}zCGI^w;q?CKw|b(o&^CmQNX5^vhy?%Ia-TmJAJ6yDK8S~# z;?eIcj+(BMj87%b>jZi0dV#ujn;b8mRj}+f%&X>G=hES{x!~3AM=C$Zt_Wc#=~1U= zVC_j{my^HD=$I!|*xG#&_to~l`I^BB#p%er4fqwvXz3*b-E8t6oiVF~K;+o33@Ifi zHo|^mOQ28_?-`v;Q%BFA*}>K{LS{qcg93$z{ghP(%ihxspQ+_EL@7L_y0sZXXBQ6> zs8}-AjknmdZP|bklO|#Qx{D81^*&*l z-X{4lbf!d~II>)z6k(XNV=j4%VjPjjg9)!2T_RQK3vO6s3HJ|;J+Sq~Yp`mR{bhBUnX;yuCdY?13=rlKiGgKqkjZ;AwnvkY=?<;4SG54b@Mbz7* z$Rzn5?8*>Qn8~G?wYQ&FKR{O>K`TCWZFESvXUzIIq zYA*%=#&1-ih7TZtX}L$yUmtSlNhek~eC={12SaL>7R6cuwU&;HhP53~B;Gzds7*GF z7vJftqPR7i?rWo_Vf~+3?Ud?}vu z$TXYgjt9LCA>ueFGY&w7Fv4A_l4o#Xq#+C*y_Pa`K3;q9LqZK~2Dyo)#+8ft&Rd2; z6Y_72AldcdypZF6z6<}71BqmKGh2g*LaT!&VWUEJS`ZON(pm_$?;+mqP=f-y5>;Mj z{=FpG%=qcX|BRma`#GjnZM{EAYxnn%kvwm5(_RElSbe|@Gx?7$$p;D1z|iLaHq8AX zZKsbb;cCJWKc-v&2EWGhIP-A-!g<_sTf8@jb{pE|t1}|YAK`IL7Ek3|afKqSX;>kt z>;Iq(Qv_%YG8=qVm}7=0T9Li&$s69}P-4C5?8JBF)clh(`dY%wQt2vK-~!hL3SxK| zW$v!fGa@Dl3W4Zqu)ziZP=5T+zHSE~?|GckD;@HFw8IMXFJ257R=RlyXy8jGU|Mp+MjuC{S-?7W-L83@t*7={Z>EX3(k;ZzuK zIw&9du~1L`zfT?P&FEl}$#e*q{>}MTe~sCUh}~XS87B~I9;*vpoG3cRyL1Wee5$&_W$21?-u@{D9Id3U=&)5IAtew%k!7P zUbHp-uQ-Id#1u$SUiJ!v7~iH^W8F)suk9+>-Kd1Fv$pHIihVx{GMxO=B|;M|l`r+2 zmY}!oK9dViP2mon?x5k1+G_v-fhB-FV+kz7mEEakWpWT>uu$X6$sXSYOk#OHjCkRl z%SZaI)%m748F+;96>kOa(F^B4Bs&BpS-4VT&d>v{v4fVy^cPFA$!@2zDcmK$Cfgky z6N~Cx^8QYf6QEbvkQ`>NzIbmrScR3hVv0Ef$lv~4p&~%1RfYSL)ZLiwu5QAF6fdlc zdeC@+UdW4*4I$f`XJ{kU8e9uIw$U%TB}EM4S;U_aG(YXx&m&2d-_i=O%kWpQJvkre zP7%j5u@`I;c(owmQ$MzK^3li-HQLvG)#vvEKCBIzOdzJB-kO)-D^*#q=OK#92y?Xj zJI&hn4S=?)ph>#G{~ukZRS+zYn*EsyAW%&U9rC!@@vW+?#6i$M%clgk8ai4pPzL;a zOy?%c_x)=7uw_!ibrbRdV{)iiGanu=& za-`FmP|V-LZak`-=;}n`nCEA{wPv(jI(hrYHuj%tmL;q}zIU$C zlD`(3@zu*RS6^^&OTSe9*@1&@t_Jx$7H4+i1Z8y&8s|@o)5X;i5rw^8?*NOsH$;R0yV%38^qaY5gF$!JcSR%xFe$&?$~=DC2chd~lj_MU~@?fL9d z0{H!F%jaiNFL`lreVl(h8u`z>ss3nZk*}O+r3Py(%1&bVz6n+6%V(%-$z)+*T+zPl z8e&SaGkwjax@Tp;mWQvx`UMC&8-oS+|JXK0d-st3{8U&BnZ{1y`~;MiE2$HjW}mG` z`qBpTrw(HImLqJ?>8!q{&h07npNlcUo{u)~6m$somIVaG;3tqV>7V45g9AwaU)Ve% z2!Ty}&5)fF=VZ%wMf!cJb7*e*eAG3r@RNi9yx8(Tqthc@|Hk*&E*rW2bLW?xV51Wd zYPx+IIxRG!0J1=KsG0)t(=Y88+H~$M9zCd!n4D$msP^yIez5`*CH9DGsn-5hnIDn1k(hifS&*8D>xe7UYO}%^Ep10TI{W&<+mk z2bRVl#pDW*dIa*9Z<0n&Qm;AQ{&&51EZWaD4rCm}rVA#V^X+S-+BzBI0VK2Q#Vw|~$PQa`LiQx)9J0g!xp+HpQQC=qZ&2LM^ zpzyd2NP2G3Hl6If4AWWF5WQXR%mYuO6EL3^h}ApLrotA9WN>|iBkxhBY|t&@Y95aw z+sFvc4Hjkx!hN9yE5#}^S(tuftwl_e;qG(rMoPzMFa1D+Xs)?za=2V}G-^TsRTnFv zP!Cf{5$IO*lr?p3o;?Z^rlV=lMShdk9UQt@RqCriCkc}r&*y5DSv6yIoo;ee&B{tD z)xXdCIs1OJ@p1fM-MBP;LoYtHdAyHBfi8SQs^IxNYIj2f)~(*gUkn|Ps)_W3_c_oP zdKC(;Z?)+o&#I3%uPEd%3s@&&`qIpA2c8ZR+mns-XfKw>m0!Rt#XZ{?eU+-O&dfsT zTW>ea-=7dYT94!{-~>vd1Tlderz-X6IB!Y?C#hZUBtrQol+@w8Dy}@4PsR2D7Oh}* zTUkF}As>C|6~YFu()G!*H-^cSwmNyz*=KWl2WP_;a6PJ?q}wiQtKsU!`$x~duG(Q4 zgZA|(T+lm85)?dle4!Oq+icAsZ;Q~AF)(8PvnzXg#U?6Ag206(sebyBw+5!Cf^oKs zV&Z(P{Jo!iQr;WESy3=sE&Quc^q>5Hu++Aje*;hb)j!NYP1h$B*ld7+Yw~W6Pq^rp z&6^?xm<1O*0s5Nk$w66Z-03<|8)>awgq?@#D3OjVUymN^zy7|0Q5~(eaN>N4n07*( zA6Eq_E&KTiGi3>c1@6J~N@ee*yw+5xl%u?&EpHp205*JOR&Yi*-7#S9A2DF%HyY*T zlyOz{cGUzOw50Yy?y{zFe4#wxZxiJM2* zB=1Yv-_Y3#lijEFK@5ps8N+GV@kI_wmvB%k5p{@yuAQ_6dg@_7 z5X5pXwR!HQiktOwiHilt<001o425&1HF!C3vvSnHvGd3el@9N#&(2+d)6u759 z&*%(VfyNOQC_3T92;HZ78OP%3*=8+ z#GXixOER^0Q!kx!rC~B$!?8NSx(8}W??`AjW2Rb*(d@BD(-`z|=astpDJ3o#JAxvV%xnX*1f5s|VNbSYy_O5@!lyQAvA3o9R62R(~=DKj(${*eAw{?L?czX+OVXAF4X4kka#-B+y2;3nKF1dZO;at^*(8gxXiQB_%7QGiT(#nFYW8f zm^0_OE^vAdDyGTv%yHIY_P@liyI#4uFw^U(8Ci%#ZR5v2i=DIt?Q!6ojSv`eU9}H% z&g9(Ugi;pv+<^h8j}6~~{eHIsyO$O3oil%y_Ac44nUy1~4FDARatf(u92$esGM92k z1pSehd^A^qij5GLuH-m{m8BDowd?Sjh5(nI z6!(-<+0|`HB-&@mZit+{4$0;%uVozPYkvMAU9dXq6H;dlc(|CPvFx}F#?-8>pY7*I|^yc;vA6LGg{ zUX3oFm7q043zg}LT-yR5kH+}UkMTmBSyEsH#p;8--f^f8p6%BaNHoD0pV!c>@(=;2 zfH~krWRZzd#XDF{m|of8B;Gr45XT%08k5-Dgzco(2@j9!Vbv*PBZWr#4eb{;sdJTw zTtBkvpb898B6gAb&iQc6>SD!jfkAPIT(q0DTm6eksBoMkQA^Xbr%emK3i1Ie_=p>& zi7bJnZcdGs#A}Z?9_eAvV7=#&ywmD{`I?BM$WV)gV@%Go3EOL^+Y<7JFj^G3FKx^| zQnXQHyQO`7msxB~db+e$96V|@|4hFfs0dAvQqFRRXh(cr?Cn$kyqCvq%hFzG_G-{U zaUMYAxls2Nho$bP*Rf%CgwS*KJh6hfuQ(Nx4CML*uq#_?sUS5(<+#YKqC5t(U!kYB z>~G)()kPY1qN6tzNm}14o%S6>_rX6TJQMxdg@}G-w$#P>Z$D?{o?CGpNzP1gd#=y& zmpKWp+GE;bQ|(mwaR4o4rWxy1?yS8|K2TpZgyz-0DhHYpqkd5(z-x_=v9e+OS1wC$y1un3|4Xo}RIXuQvD=zqu484hhRer2L{t}V9#L?~JbOOi`pHlGc+;pFi5 zhMpB9(rqO&D=yxk-^hpH>-HE(S1`v5tO2i&HTe`MJilme?2ubq$VA_3;ME+T2{Bz7%w=V4FRPR>8%ljuKeiT zzX&G3DHIsEyb7}uolP)4&?gf=5&$yFPqg&Z#Z*3DBS$b?3-6}WH@?g#;vs7DAbkoZ zk&4}f5%r)obt!Ch4YoylP%kCHMsqe|S~lTjs^E-l&3!|@syz$Rn9Ty_1JYi|#^oY& z!Fc61{v8GuZfRV7KK(Nw7HZrP_a!Pds`W2OwAZ(|fL#xUN1h;VfGeK^lzU2w(BELaN(yQ`=YFCfc_ zGUEPfuM3+?d165y6j(tM6)BDDLg~*CaGOx#;Rk$Z#vhc4D84t1m%=D&%hx@kg@QP^ zpqZ%x%(XzHQjWO|Pj{3-LhZR!QWTI3R>s)@Z%p84cqWmKS>vQm{3v>@^x)qp?!W&wR)=dxltlkupPE<Mi zUrXNgCwaUL{wgBcnX}c+iIuF(ZwS8-lhCgemOKA1(yE!@C*)pqMtBP6D~$pko-#7q z*OZUBjFZ>1)6JgYEuZB63%4M0a}9f7%|(wMhpAy)w2_8+N49i6x#PhL-Z~2NYgAHE zTQunf|HJK>^fYDBg}0v-?OwERI^F9w z3^JT^if40|8O7V1MIW-WKAes#3X-ufjZQ>&rg$0_Kux>5S?c~M+5}n)p+a0Ql*c39 zSr#uzG-z#pPnYh*rx-e4040^(08mJ-dIG_vM2PeJH$Z<8x(2xqso_P&vGkBd1qQG` zI`KTXygm8KW#8#?)nJTG0#zHe9lflMOt8W*o^T^BQIy{E^i4F@XK>F)kWvjo_fB_w z`hX@89kPoDY!Sm}^3e3~A505aa+$+x&_u3S_J%f)w5T;0g9U9B7bZI9wH~h$RS|M z*@h{}Ja5BPeSm-O77_TO|%&ID)Q zlo4b%I$gk*%sq?dmZNH=hi`oR1ol8Wm+vFJKjy-|cBDAH#jZyc8DXZrHEhJ2+5jyh z(n}LyNZ36;mF%rZ9aCMNqc9L@lAS!|T~A7h07ON!fW*nn(gumw9r=C{uAlVP~r0zR*~(q)>29B z5Tl=?GRd&3H}^tHJ4X{U)qe&JoLc%Z6(S?yq&o?j^Lnl&uPbJO3Z$|pN8CVCM-8p- zHC&ps9~^tCZ1BeV_~wNofd#peGB|2nRXdcRskz9(*gx$L4jLAZGdgPaD?#bR`PMO3 z>7h2Ef_#@_#hzHkK{>Fa8eJsvo>--20ZK8Pjjc0)CiT|pYhk_*WfI!)&e|4UOU%qv zBVS%Q&0vWg)M0y_0Ex#+1r1BlIR0(OX)1g;i%rWJS|ex#98P6-cV$Z@8#3pIFcLXW zwi!)HW)qLonNA2*4;z3;I|DR!``c~bH4`1oMqaZ6OBU zeaUl&*!t*1L0fBk|02^UoAi>Q*sbqnp-`|f6719U^qfxvCc&}hCqVFqV22CDoMX#7xc<&4uv=h|v2 z(m#f@)s#-EeILvNFG2if55IY4I6+b8!`bzD>>>=G3J{SynO(ShX!cd1N|b^8K-1s+ zz12mG+kD4|GM+v8LfG`4nQM@4fBioj>*@C|p%y-$ z{*@;4*0hyRjB3$J^jpj+c_{3T5JY+hs_A69_$98B9u)(tG@QE6qR$=-ai@2e%G%DP z1qJ7ipOnH#I-CKfK7ZyDq5}*3HeH_uj3;1dB}saK^icI314Sg+ZBy+HsT)HsKfw~P z5p`^}wdOBT2^r~AU8j~5$q6$o+!&y0P-MhWlMZRO?x1BTkl+A1m2-7PbIHIclpf^Q zQiNDRXC<)%-gYLELV1G2rZ4xG#jB(V5z?`DOI^Wm!(1l z#TqIk%OLrzDz2;?ARfs|jGZqoj@_)#rxK9QjT?6EKBa;sKZ^ED#f!2wGZx!v%r#FN z)`drdAtv}~!rd|ehjVZ|>oWlSKgO7?Jn41PZ*Pykp6wG2e~gk{*Bau zb${fca*Z=~by(g#7^q_Ne>iYA3dERE)+fb(OBf~SP7c>2E>u=2$?VOJRyzHHiZHdH zdQn=p3LVzj)>_oVmA$KqWTRU~Tzb;i!f z@C}0qmqGU6m8?jq9LD1gmF9=nTnYkO7a^X%`^aUCvoE`{hl=6Ww z%gZ)MDR7-Un1FS{g)9>0(bxRH(XkOt#!o^e|?ZLfmSneUz+Pxj$1VczfXn-N3sRNkaQCKB~cXE#~gY#4bM zZDVX1Eo!~?v`3VI?JzELh}-|kY64l2+hS-xDdtD`NYCj(d|)9hknW;h7jUG~m}P%2 z=WbW&jnUoU%z_3M4EbyJG;U%J4eBF`YdC${RX7wY9O~SGQ5LxmUCB*Zgx9ICT;!x{ zP%r2a+(Y66Oe{*?VEayEiY=NG>c3pI$y#;eHP|t3B_+ z;-7TMX*hE2P^5!7-N@ucn2a1_MQpc2Fd$GA3A>kaZlTy$N8@H71S2?)-u18@&tQQS zs^D?>VD^r~>33E7Ddt+ukKC_lZlbAn(G>pHMk8HK-1FdsQyD$)t+c~NBiAmiT1t9Y zdHdkN%agv{zpbsnLa{F167OL4i967f>XAztYwNrkFG3m8j#o^wny72Q+8SNXC(Yi#e&Q@;ga7u*4MK z;$ZuRIes^NgR*fRvc%F$R3!?R6dwkqQDT+C?^=+qK-h z(cx%!-HJ#GQh(dpuBM$+Jv@yVQt!l{Tq@U<3+5~Z6idumAqZo^cj^))HU9J^8VHns zrzKcA(D=D|Gf&UNBNPI`Pfz&k$jHDgg6f!EM3vCW*3(8#_;WuQ*o_-)b$Q5KYb zI=|;s^A;movXaq)7gM6TWhr@?|BK2v6a6+|qB?zM?ru)E*%ZU8N0g4+kP&thA373u z!-eK6YC!|?9;Z6lWxV&92A{sB?MYq^%?{z|1~##KJ(glz=vq*hH)^?fQk{^I%Q}PY zF{X}2wT;=bU2mmrX1r6JW%k0vBoI(is+&w}T5_d}QMA^yaI zl~TNi2Y(->W&IV0bhYMa%h$S3xwk5D0gwgh2h!omu+6!`Sg6C^*cM9f`RDw#SunG} zQ&y(8BJ1X!^UmF%hIZUQ(&OIEl^UE5$fkb(X;t(%v$x*74n)-6E%Q1Dmm_e*bMdn^j7U7yB4Xm)OBpmf(D^xj={K zpW&cZ9*iwe07QJ%^7NGhk`FQD{Q)q1_Gogvytb;{qEk~LP9Ke1|{0TsU- zFRa4aG~Ri_EQd>B>s+T0x1!B4`EI%-4II5;j=wNTNT@}4&stOKM1a=k3zWfO^z1$c z-I6(cekkl+2S#Zl`xF|d*vU}o2XKb$gK0CLWlg-sshaFC z6ltw$`}ch?PYa#hx<%o=2odm~p{lR(uq&U{5MY6LSkFVwMudqt-<2-w#)9PpB4uI? z^WOitdC!eIPVo*wS_?v7 zUt^STOFSr*bR#iu;eRhhn+nH!alZX}L!z)X2$HAJ5tRM5>+%1KblaFcCGwhEm%q1_ z|3yU&$E_bX;HjsQN$mjW+6g@`LQBZtbtr$3f4tsw_}H^;(lL_LU8#ie41&Bm&R z`?&5eVyk0?6v9mq?XFP_o*TogFL=T%9CbAK(L_z~N;)lvr_^dRxXsupT)G0`#zDd2 zA**zX1|I}r$-pWb+a8j9Cis&LSvULo<;4>w+(#H<;LxzKLh3 zZt4{Og+qXTAvBff0*GnvVj@Ej|3KOsOf_Faq6Af&juGLAVx+!7JR=m4NU~2WP zbA8ZQYN8lJZ4G^A*HoN%^kKe8$0}A@cgq=+ZZ_I1MXH8pRO&NHIF$UwE%EbR(-iMA zomZ7uH5{Wd!Sxs~V({wcsb;M|o@vs|IOu}_8p3y6A9p>}4_;!_-zsdAkDV7E$_KA= z(=luwYLj@w?JFL5b=rT~hW{U3|5;k&HwvXy~kCpt^Ro&SZSFW6bp{A=2xhP7zv|OG%kS36Bh0KlZN(tw1{ZkRi4#!RuV~E%_1v;Y(q$C3qIJZWRvS( zp=rN+Kx0$=(5e-4NA#!CNW8Lx5_q(F_c2+GiH#BB%}59b)0_wUbGm6@#cPP?i_cFR zLnf34HtGy*biU5U{^0+@fvg|ncD@y7KOlTXhE~>l;vz1g=)aQgM}CHlxYBqNWzRZr zCS3FRBOb1t;HXbLm~&cBP+1A#s!z7EU<*>T2$W`yyMIb)6Y-hO7q%}z1ZNh>S%N_V zY&7Nz(AbMlDQP$2EmTu>s5In+L*_%E_cwvfX$C!e+D%(I=h7*ES+UXNYpcfMBk{-hyuXnB{Qy zXf$UAh3IJTrk1^Ou zOHtIWQGmg60002r3FPUTU4({%8`Z_}sUI4u;7Z#vS%}y=yQAot5zAMX;xJa@Haj9z za2&uCJxgvC3q~0vfK8rKG<5lhMZc9EfV`+TAlXmOiwe$b+EyrCYnMs+%SMBccc+?4 z8ENF0aZ$o9u|>$n{WpY&)W}SzFGJ<}xIUbH$ zcE$a~)88BUiy%LH(YIlVmMvJ92(XMC@?eVR3S*?SyJ>9RQ0ms)iSZ*O4e}8dwA{qG z4uJdRY(cd7@^@+?cIG*K!NCWy_Zra5Gl##XiIJMd-X?>c_eMm7VD@25BUCndp3es_ zQ6n|RhI42u-K(0BC$g8z@&`Ywu}&sMn*pgb0g{v@%SdrPapZDf9lE$If$vTMJ@rwz zxrn=0t{)h^#mhDoaKT8}`w2=;CvP#5KQdGTh^S^%q=SqTp_%+^MdxcFOR;TJUgr2$ z-*U^kYdHUh01^V8f+5$m_iZ7a<_I!vNtt{dnuk-h)0r3|j4^w-&}X|+Ub-O6pvbE5 z>512X2xloJh=n3%4<;wqb=2;*{j91Bwj$^Ko;n(9EOg0(Lb&C{7|Ait>#WaGEebF# z$4jh{+UoZcb%#ZtFhWltk#b#z{iG#5*Y0!f{~#43Yri8S!U6YRPM8Ro6(YayzstlE zp4Y=tJFj#_6&n`w)i@~~tA?h`ZSP~6gaAiCxW9cDW_1HwbPF;q;U+m~gZ3-Udaf2@ z{M3Vh$9(T!jVnKv%%Rp{Oxcq5XJwzV3(f$}8zfkAy2)(qGn+7vl!Vc0qwgUnF}M5c zn!@l%)fKOHkFpxX_wj-Zw@AR>$*#!}TGhaK$_bRC3hgOR>U3PNyxTs1>~FgJZ<_dU zmP#~xE~w|M>zD&i;0E815{Rtp)D|)YAh>t{i=4OIN~)c9OMSfrOyM4#%LMv6@3vmh%Di5rYbL~*W|ivYaA9=}*vXWp&REjX`5bUn4?!AZ}E7)9`r z#kx#^!!UKmj#GdpYGSwx{|VF)FaKLw#dB0Z&GQT5DMu^pJjgH0M}a(2sl9Q|Vr!sc z6qO>qy{M@&{X*RLNX;1%Dyf8pm3OQ4pDr*tbV%&1s$tS z#R8>aK-Xn@T<{RAL!nlTGz{1SXLzse1O-ZShx4OQaE*ln>N6gk)e=pn> zli(;r@|k?6oaYll)eiOY$qNtHFTnab04k~KBxEsKSXqt9`H7j4R=p^&Uu+zD7}0cs znUx;Hm1claD)0JC`HM1wEe&MOPwCM+aNQmihq>n4Mr(F#(=P%NK7a4I1j;hf0J9Kb!s6qb&Yd5tYiQ$mHbD??)<^VW!mSrs< ztEIh2N$kG%n`%y0l=l0wNg@K`sF_7y8Jxc?ef~vW`g$Ecy#Sv!<5_xUSF-oJ!Mz16 z$31oWj5%m6=K<@*G7$^9TPn2 z&%SV!%P+No)m``A-pGQXxt*y#9rxx@`@YT5U1F7p1#@8%T_5bImuFX>j?bZ;J}47P zj*BbM;1=2bF;4@zCQ2E+_g)cfQ5I#-SH{)RjLE?eoP4X* zoH}s2f*t4JY*mm?5RoDRPvKaywATOAUn-dgy)?vLNCVHoFC?7$t$f|)oelgXWhEh~ zB@i7=^O{v4gz|j+U*K#2ZIgM7_#fiu^8b&qaN%@3&stP9_IOx z{dkeP)nmhP;`|YLa(Rx>hVi*?K89CP*DnS*bw?fuoYYEKf4ix>)#$j=Wz+$%!y%8( zA1~5d8)OjFF{<9#YgODd%Iqs{~{LOBd}B5qUm zj!1Iysp5Pz!M*Ce!*@!`;ITO`+pXX{NO^l<%8UfIOej3V|&wZ3VMgm*K zNnWD-Dh`t;WNgndyCA6w5nT^G8NrlQoT5U=M<6Z@l#22GMQ!Q9%GD*nj$LxhjojnD zQU<|kJ1yufn+~-;-Fr^q^K{1@p!+xGCmeAj9$i91Av=ONYmpteHkyO+GZ8X169;&! zR=`j$_f)#jN7v)`eZHq0Dp8T2NqLolRWjQ4=Ggv><%nR{J8*{z&_?$04W}Jddm(Ic z`NSzZ@Re4fm>xnZz)yM7CGlE8GULDyPHm_Ajof$9f>ZEa6|lda*em`Izg-3 z2Se)t#CTUi+ykZQ8G1`v6no7m5%1*NL?v*}RD%ExGr?nrAEMtAm6Z==StjuzZXL+# z&jhwjHr<<>=8>j8)q(7o#18xj)9*pd5{EipbQ)aHCSr%`&bg2jVV(-`UXwQ<31(gP4ES`J zwHy*tKZ!U3-bSDwdmajWw?_0dhG;R77AY00p2u__&iIA;*dmlzaGzdZ1LkFUDBH;| zfqH~EpiX!9;P%a}XmslHvJJ75g8xziyE?|mc0N{qx-v}0Ys*A1o(puz_w8F5bW_-? z{v|UZ!2HA`-gw3}4i~D!a)eQ~p`t?0aZO1NPsLU5Q16D5K5yyT)x@G;63S_a^-1UD zl6B{NTP22_hFqWr^-`8yan_X@r2YJp4U($pNZ-b~;E6Sv^n{yGVeaW1Hmr$uAmMG6 zB4K?LDc-{v(p5>ramz?Vhk3AxJ14SeD+^g{1@2Pc{zqj?$t%vQ4rDV+0>mHKRvaN$ zmaYLyPaQ0P`kD23abXH3+8K3bPf*K-=KnCQnLNgNALiMgsIM#KuS0YO=cs)`9E>?Z(eZb6ZGHMbMrv#7#dN&@C4i%d1e^kDj&I!`F*g@0 zeDQ?T>FwfSy(34r$6RqY$z@*EoqaP`w7<@*m_PWSdWK{kHA&LEns;LV+U?Q0zPGf|3mS-G0Lc1-K!H^w zF{xraI^z_%LO>m(jmTYkBoZ%>B8phx^ViBzL?W)?d6~Osee5@~DD^au=;@k_KK{83 z&62L{qD%A}l>%wpog~{}_Wy3@9|!Ia!KbKB=?HXe069774)t!Bd&7W@1vHO4!dQ+~ zfWe8^Fa%SrA949P`NFUTQl>T? z{l(`9&XaLvxzHEuRD|fyl=-C!vB>L#u3^;vI9efkvTZBVfmOEhxH|@r@0@SjZSa=k zbb9zKw$crd8t48OMq@NcgcN;Bc>+?A-K0dLy)Ka;+hEHXXPN)Sfk*6r98%_`Z@Dj$ zzTisj?#VLAzjl7gy>8I>b%sR>*huTIKKd5$v63>PtETGcJg={8)aY(Sr`{hB4A188 zMg!=QYpIhLk>r0Jg~MN?1xR=XpiQWYi7UzBWvNvB@ywl%#G(LAMdML=Dp;%6koWG< zpv9S)ILc1Y} zh2;~pQ1qUC`|bfp*f!8f#wgq?1HQT~U9!pcCtgTz+}0TJKr-45zyNx_VZZZM1px9` zAxGOX~?1PFOoRe%Ld+KupKn8Dtx|lK#8wdZN zUIn$udCCNW3M)g03xTJ>TUcHodH+}dSa8d7564(hSDdHr-5LYBXbxZ$3Kp7L>d{Xc zNXJL2t~oBUJi1D5w#-(_O0+NntD%c!nka7;s9*$eTzO)9CisT(*$i zBh^VsycWLJu{P15k4{y5K#5%b*Cm|NdhL=GQnsLi=TMn{g{sbuxh6Ed)X-4Fa)1B; zrk??Q9-U4~{kQIo?&G5%PFzLTSq#YwFS5YMSapH}i z1XDg?wEb$Gxz4C zju}EB;Wx(fVfcu)Ci((5QV%~(iQB0UhC8NA6#|7-Y^t7ZRhN9+87dbb%$oj-y6~{< zT`SDfFyG^`zN?XAZLC+qbD=GL)2jWa1Cuz2J-^o{`YFmv$``44Y&0lH}6z!J2TVpkR-(EBAu}0|5us*H-)7PjN`*gWq3bC;z9*ySL+30{ z3O+hxHQz7}{JqG%I`Nd_S!~225xaIosS|e_Wll~4Zt6Xhgh<~{p?-Nd-?y_|Bo9O6 zmbD8v*}C)M30hpkm~|uLXdCPkh;OJeDicKoi*QsP!gJG(2bT1^Y35~Q@51_gQ?{b5 zePN+Sv|9<}gR8Yif~DhZ4B&snH{mU|x`$dtp-5}|btRUoPwONoi&N^Ij8ADobe1}) z54iCmy-B)pE1h1Fp?pmemTOwIFbv^SQ`5?3&tO!;XsH^lLkXpP~w2x$OthNyQ)H5$XsW1ZWGFZ6_Z-gR2;~{h3Cx zi9r2U|=&gXT? zCi2WwO zoCY2NS-Iz6JwN&u{=0I5qp80?A^U9vWLV4?POj(m>xDnJ6ApPX{gyh@jHG7KJ{H1V z)5eJX8f#n;pyRlq|0om{?98sTgl)TQDw&yU1?Rp1H-vRWU~2rai1bOQOn@y$?kE-? z#I9Ck#hk)K@Lst~NoJ>IE|b@a?nQ~~vZrIE6DC!J@cqMA!JrDe&S!nu;3dEe%wD^e@|q-v0tWSdYFTwZ~Gn zXXsY-F5sidr;IX3C#Dr#^|fktofP_a@61<8$N+SNg5Wb0>YXol|C44soWyNFXWW_? z4JcnW#!kT8TA4L5L5rx{?c?*%U86=Fhx|(&$Sgf$dpkL?+feNJDu5^s4FnfNQPs)< z?i%_Li%JVl5kzc*&ipn+2|nykLPll?m*38*ah%|81! zQ}#VdP=a)Z{MAZWT5XE%LQ}ZSdO91B{+(8mvvC^pe7TB!rlM|GYa(fFrClnZM5IoS zdqxV#o(Lz!w}*1P@M=-kL0VVMO9%KaZ!<(}!hL127Sckd*qB^~5xy88@L!@_a|Ti2 z@gZW=$}bdG&@udr-QAE0YT>s^ub5YhNY%7$*etd_ouS-KY|=KDtQ%0K(vNl~_&R)l z{?ZsGkhF+UkkMm5wIxoV();o8ESi1Y=v+HH>9#W3Y`VB_egx)cSEGy(J!Uvt_&c8*KI*9f zHnQpbRWb7k80*+szB`m0?4qkl1$DT81Tc`~8(q@C-@c%p z2c2A0>n=~-C4_vgYdL$yHt^sW1rlvZ`9~X>dMUWgsZBmZM}Jte+2AXN>TI@40!)$p z;S2?CQBd}4^td1r>YDKoOIyRJ1Z|Kg8ru#Vb(mYV?=YwU%CmVSYG17Wu8M>2A;kn@+X0ap$Diri;c%r@w<#K_c$Ve)%ws>Y)saPmKbI7P?{%Hn z$&}IH!WE(g(_>?9a-wr5q+nO|`bDIE?SJ&tOUk3bg!^S6-~ykL0{>$rI#H6mjU)x($eSg{Y!Js0}6|d=Ag5rLSM|aMSz|TnmH4ZNaG{Oucj!O#3acC zUp?4@1ZNW}3%wGhiV8qbApI~|Dp?*1S@-1@8o3X;n5;Zwx+Nt=p?RNLml{O2{T=g# zhKAgd7hWl(F)XAilx@Cw`16Wnz?S~TP$j`W?Lx92?&+uGAr*`%UXpt9j8V9^@X{J7 z5VtIFDsxKU@t0iQW1wjqBTER9oTu9Zk1~SDgN~&0H)YG88%JmBzqXkfbv#LtcTP=o z1mG%SSla>!UTgnf&V4HU;b1Bop%C>SipcC&H1zJ{E#M7ux3K{YfY6g?=05vglo87noyk1;sKuA zNB4Q7e~9H7A!N1zT!#MkCY*+83EI0fpqyQ?-K^b6A4qZA(jKD}h6ftW8e z_yytE2OSw60a6)xrEO4O{6P1U!opP1R`pKKdN+-u0;K$ZHIK_+mf&WsfBbeoL*eix zRqWQvUcl+11vM?>L&?zA%n;0V4(7TO*7(dFm$ouQ1)BNB&~ebDQ2VdUxtz8NyUl!0?C>xpWT}X?hDVq>c=QFg&=BMKwFQK^L$T9_zy^0}@Gcx%cIfxvS-JDMS>S_b7<8Ajz-6NP#jSq&W0&O*qOweVu-_ z$Jr=ZYg#qtGYY!YbZ<-g!IAtR0>91FQg}tiIOjIkd`JRV!(k^rMzpX0r!Fxa)K%)- zaNkU;jh^&mt$jDa)^N*0Ermto+HB&r9pOC%Kb_`dOU@JbKezRsvfpfH@_xI%fSPd* zwuAYZGTO@D{4R-q|B7JlstG*zRRasZiUl=%(_y|HHC5V6Ae(CN0GEgsIPw}O^=5e1 z@-~F?Ej=+u55EV$miq_d^{pU5@u?E0B5&pM8v zmmA#k!7Rgo1BhYI3mz#+Uxk@i1!gWol)fbA@Pn95SoMi<-O?;}Q1jsXI$>miduDoj zxSccbUtC$FTm5ZjXR$^Sh(P_~cWux#yAxv#dEazuax6s988=oPaS~v93Rn0B;wP+t zw$D&-=a^1K{SCd(Bp(+105w<6^C__GGNeE1DXh4>Q36Y@+Y$JnNyNK!|J%6;IqmO; z0ivbO{4GX5>VxJ2=ty$tGRT$171Rye86GPhwsnrSRQwLj=mkohKNTZq-Y5M-@i4{( z5y63nisfV}ZG4uDK!796U9fwmc39#>Sqj_!w`reR<{;{BEGg!NkwdOJ(C-?xn}2%5 zd&*BtG48iBxg%{FGBkcn`!%NsJeorbVn)`c5Mz|pz{@n0oS3`pu1fI#z|05*7o>;f zx@^eSFvt%t;{6HduHoSr2cIF47dp!u4wF?rT4?jHx23dLw6xK_Dp#LP*k7hlkmDK4 z%B!`YasxFime!?tV9U2%i{FdtcYV9WB+qC-;0a^uF%__pSCwNV@$kyVj2M$gEiVx8 z`vKfkCpFhr!=6KL45hQ!+duwLF}HFfVuSF5rjGyruwjD~-}Zb!X({++^5^e~v1~4n zTZrX;Fn0Wrst>@vw7|d+L{Y9Rr&DnQ`9%HO=FsX5&SGw|oF8_@vho{6-GVkK=t?7I zS9&PdqX$Kv0+~F2Qtx-{&O&N?j6Hgt^n;Lk1;*hRS>A`Kof0c65vee=;&kUtzae`6 zzoIzkNcjQ_EJ`sV?Gj-ZwrV$jaNMpymOD&C zK3y)?HBc@AmZF2O)#vfR;dL}>El0g|sCQ%YtchjS41?O5#tj}<*Q#4-CUy1Y-rJ?W zoa4qGobl_}>kv|c{*%sswW_(i`&%C(_(|9<@SC~_2Y#8pQ$oRh8LdsQQcPoGJHhjr_E zAT?)5iFucM>Yx8PQBy#tKtd9{g@hx}Kua&+PoF1f{!Oyx*V}FQr-P@h&OF(+_2~`C zOE~_UG9p$(W8jlktYx|AqlpFPWu(9GMG4=WeFq^LtF3@b;nbY>UbNdm_2-jF>2DGo z;n>%yNrDyc+?PN_bTy<1XAG<3i$MGSv`NRqJ{a4Z%5X0qY`8iU&M{R9+k8&(dB42| zmJzql&KZYtb%CHJum>!BYr0gb<>5bN0KV@YjdPjft%*Ql8jY&r`^C2b^z&%0vyZ0vYqJ$V zw^6p8`@2w5j}-L#LcaLC!dkP!d+zSv#;z5`D=2xb{jiP^B)2OPS>?;s#q0-O-^t3E z!sV!yxI|Fbz@(iVFW|2_l7!HpQl`s>MKsDkBZ)pEy(d6w|4VWawzfKWPaZe@Xag z^FvWk)h;b4x=7Z6w`Z8g1y5Hq?qO%5`z*5l43?H0n#8dMp2+Zna&}M3hFD4f8AczGuXkyp~~>!1j1ykj`gE zY9CX{HGn;|a79mfb))GLmztQ7do+nU(~C{{g9ecfqvgJ3|2?OySuk=gZ)B}$ zPuVC5E)K7%y6M0nS`DlwYTFuBsky`=8Q=}QN^)HI*E(KUTmPz85q_Ydix1>9y%Joh zoPeYh2I?lmC^HgjX}qTfugq(nG*fp%R|+0G(dPi=o1R!9fU4{XcU)`x>)?C5!F(x4 zn~018%s%hdsHfz3XahTy2b6`h`>64hVjTrd_&sw!rNx3EWz-5ZH72YXVu>m7$N&we zaKrR1r_RrIQF~+UZM1jyu_t+M>?Sd|*BfIMI=Rs-p^&WL7!Jo6vy%G(t|HVgR6gr; zX@>+&TlG4QTABv`uLvVS?-Lwj8adg10qs>(f>ElxN8NkVl`{2;*ogP7H}$$ zvLK%XzUf9{c8+7EAJmj=YzMt^bGOM*Pd$i1KS=B1Xn*pYtGRiX0jd}}HTe@t*MH%W zR|$mWApHTwcW0mc{sO8(E+{RBD^KUJ0s48?di1H z*|t+J;gn*wM`;0(e~*e>JI`biRQ*PE=pO8lGeyywZQSyuOx{o(BDzg9!4rSbQU0kxHx3yJq+A z3L6F5W5Y(NIbwXG=hWg${slo`MNdW8iKZusTV)#R;DsvK=G_Kg2SCf@~HiTzj!IU#|kwAc+DX0o)bghWe{MePbw z{XGZ$$hZ!|)H|peKI1h$4V%76B5>~QYottbZMB$#mf|bK+u$8`?beAiv>2KCJxUV& zl1FdYp655#^$>$%<@*0gyF_HVqgv|%Iuamf(+7Os`ZHFDzH`K&1<%~O(KsS5MhaR{ z4w&J(QRke&!> zG*RGm`_cbA5i$#;aSWJ=9z|%$LqRrtU!Inq!z1akV_@wd>^oS5HUi(6!shFP75c_r z<0%Ntu`am#B@y|(t<~f62RwD*;auu6_uv2j>%^M+rjA4J*jN$MLThJsI*$SJNd<2^OnR;NwU#8#UHL?7Ub8A!%L0bW zMb3cM{W1bwvR2#B5gd~KeFpW#mgi&qP%d5~nUOBeeM7Ayw%F&2Jd?kHHz{4-bAyr{+o}t1y zBdi0cA%p$~cEyB&a!-$xp(}c!eFMSrU^_gw48eJDBLx77ng=1qg?f<*7OrA>Q`iDD ze%jylL2UPTO%D0XJO9Jzq2>7oX&vVDIS#-`=q|*c@=sn&-01yC>cK!R|BwG_L1f#h zXtu+MkuwbD8$%`B^rYFYM_}*A@VnoV5q%CgnD*VTv{90oAQ}iNy^`0^CrZygkr#!6 z5cp7d5(96h*NQh*8GYI4u0Un6`ewAG^ty2WJ5GYX>Ueo2)D;hW`g?1)6UFWKdH{AuOu_{wHGQKZ zL&}Fy$?f4={uI|!CMeZG@PcsNuQEXOdKLYf$CbfFq*}}ffn%EZ7$3Ao@^G%^r_322 z;jVRic<&9>J*w|%hkHuZc1{o-au*C8xqmx~A@(NVMX~fNbjXV7=-2KdeJ9d_ZWYtR z*n$mb1679CX`bi7Y!ZC(O3y^N!Jl^Vxa)hDI{u-QrkA4lzaJ0lX$Z)K{)q!#mt-KE`8E)V1+t5E?8Yhytg#n#wQMZ46GtyBqCm3Q zwpu3s!um`lotNxhV^+uvt$+C3o!Fk^rHKjMb?4|L5d|3%*p5`wKK?h;Zm z(^o}NJMUcKV#0TB{y+8{Lj|T%P|!Vh3=S{>8ueMxN3^G4WT)_tyi|2K9wI=tBJl4W zMuBQSS^>=s?y*Ifhsp_CZ^}Tx_Q2PzsZoRF|GGPNc3*!PTKu4l0F!nEtYghn69K5! z@hyTKR8n`B0P6%oFaTV*DAMHft6fg*elnR+4791;UGcM&x|fyb?@Pvwd)%3Sx5 zvK^Z7t&~vNGywoA>g{`@^6lk752UzXZF4j*M_}V|6w1+tmu$Hc9BsFt>tp!wJ2dMm zE3zbE+Sqk842R&Lbp5` zu(4cV^**OaG*d+(LjEzSbMoVoFo9!@R+Wm02!I@NM2Gw4mV>V0sm3g-kHYc9?u{#& zzr)SqG2}?R@M3U8zA}^N@dZ#_*lNJlWyUwJLe@=)(A}@vyhfpu$++vp1v~6<8L32* zhP!o61(W1STPVJXh$$))0JFlTuy%$g$bU6}g3zLBSTl zn1p~9*-@MF7M)*IWLNJwxTKv3Nbh>a$Nr^IrL*8?*H^*nSZf|s>B+5Lt@59^ae61a zzr)B(5Oni6Z=NL>nQ<^ofa@a8ct-h3nvmkjSJ3R3oS<#3Zn!xx@Xlqz@x1d;IU;cE z=sb`eic3m9M%d4wJ|HNz~t=x^Fte#=y%Lh6Vho~C6zt68kOsuoA zB%k*Sw+9`A67`t0(cbQZ^|3qvA(#a**U?hjZGCuW$G1_Sdo6x_OzO^ z<4w08ry&4^OHFbr>ej&Jrj?WIVgW^ykuVvVZ>#6NRbnMd>;WIXx6%rz=#FV~*P8z|z$wD-s} zDGLf_l)WSc^3V7g`p0Xe$z+M+iorAc+nH1@VkQCHW!Yu%T@DJ6f@$;-M|(A8hO*1` z2S&b*M{l#tz<^7@Nn~d32n)FX>P=<^xRe-rf65Z{ z0voIh@agRh!2aT>^zFIeg7rEX^4a@njAk257x9-lI{uUxmn2IW1#V6=+S65aSVUcs zWl9U`JcIN6fJKs=0rnBWxD!`YLjw$NZ?v8kiY6&*Hw`zyqV8}~NC4&g3(^Q!L>AHp zw>&A}ct|t&QW#E(?B( zMdUBBSqW!aoO71Q3D6I^^U;ivgHinFu#{e6fFXs>LrzbLuC8k;y2<0L^0tVon23DD zmizO%*vHz1{r#=GJA~h2A;woOLu`}-e$Y4r+Xgm9QnlFFyYOnC>i*I}j3ulDLX#AO zaA4U22Zpk0kTgZhyonMI>1IZBuerm3Q7)OFx`e8QfQq4Ajx6-lH%#(}K?ouL2ITTd z;)=B{#|CZV`~_poDd*xE05E)YqOXYv^)2;n6&sJwMJ;*jZ=2IwaFjJam=$Y7i)lyX zBD?GEC+vYuI__9?>}g>DMG6Bxe<$q$PPSGMPp{k8|EUQEJUaGTdW+$!q+UFXJZAM6 zK@n%_{j{zng`}N(G@P`3?R_kv+=9}w$A3-RDlGZyf+!uuSyOu9b#4lU_8#5zHCI_{ z#xi6|>v!3B)qk+-;b1!cm`(@jui-zruzz)A9ORz*JIa1a^P!P)rNysddTSfagxiSa@CWCv%k?Y{XQ$E24oAr6t z8}8h5qTu1#`Z>$8a=mwfVAYn85vzVh*UhX+bhjPhiUgFpO@oebZUIuqdgr^)r!9SX z5J~D$_e&See&yBT>dej4^{BPsLM!P2`j`91Pu@L35F2jMuFR6Qev5HbjVZ!L^6Q!2 zm?t|xY$Je?q(s4!2k_?+UCIc_Ld>vd!Eg%Zgu@{Vgdmr_Cga0FeaE*z+7XBQkF;<; z`3n{Au+`O7cnR%2h3xUG8Q*dt_J0tPW6k}+hYnB=H23Wa-)*t@_9y)Rz2Q)@n~W$& zXpZ#Gnjr=!u(apb>%|&G{+-7hwm80TmV2&)M(;PJK}7m^qaoF4YgE$JUC4|7A?vBA zqkPS_sBf*M2{18ZY;oAL9VYEN6^eD&Kp-lcGehnffkt2Eclm1x)kuS|oymNJ=5#sm zmwhCgK9LtbM&q%HK*33Uk0>5tcT@^b95No+(+PYrfnQcsL;(fA))29y4X^e?nr@2H zj6OBrRf-PwMH4r=V7tM{v)vdr4z-*KGCaaB%c~}#H-mh+o3@Rtb&j+SNlXg2Z4@ax zS|D2+=;%$}Qa<0vmT|5Op6dw(pr#3Otf znJ15)6wevJPsLFw9D`c?DUbxk4x^SXm}2lfkj@?`HkjzaXR^F)q`~%_l9?nCTQw_j zj^<7X&I>ONrFnW)(6GMN{s7jWFF|n~{{gBK%w0drFR9c06(WvND(9UZL4ksS*HtBv zPC>g_WApQlM-7o$2OZN*|JwM+Tjc-k)O|dn54XXS`PGFon`qF1H8R9`q-IcS(u7q{ z$v?czVb*p|jBEGLe+XKS!@NO|-+!EPB1Ms?99L<0hI_iYW%3BBy%yz);D0Zsrtc!eTU4^1r*|cUpVhTcPPC zRO+maOSpr%B0%z^wIKNbKUoXdIiXcg@|Qwa0Mnt;UZr&6B_tVYbKt}KXeD!{R~1Ng zo0H9VL3F#gF@bc4x#7~~=u}5lYxW&Izq>~Ic$9Z9o$u&AWF0Tb%;=0KDqt_pt`ekD z?9P88i4UryN0nw!IhF6J59T?smYGtdaO%r2YH&H^v#J zraNZ1eV-NGWE|ZlcUv~xlLY>Pk}j<{d}Po0dTH}fCc1-?o5(_OYmpwKsUk+JqCy<# zrG5lKt<(|YCw^|>rHp-UO044$_s0vBg^yFh9s`5@4Tt&L8BQEGe$!j>Dyp zPPZaoFcMS4{F@K7NPbdXQxpbQl_=QyFQ>z!!;q(;%5YEi9~n^N6s=Kl|D{t4{0ZU{ znxR=q%+3ACPA>(%uTop5xSg%yW-71p@F)MK?MDM2^IN1m6&r2|%#r&`j*0g0HiMz` z+P0ypfx(g}(p;MU(QoWT2#Pl*R$5v1%2l++fQ#~cC3!GS2T&uS{EK#%8n5OItSIBp zo6F-G)nInm6t1?yy%T-wXlz|YhFNhNFPjkRs70_*yb%N$xJLhNX{t%#4FH=UWA5W7 zjgf!NRpK8NLAI`^$hc5CkXH$qD{=?Mnhc(B;1u8TV24tZU^g1;0Ii~z@D`@}3dUB3 z%0h_0dF9^Z|69%uItEOHI7?Jnhfw2Lp57KXbUDFtCekMZ0zi7KQ5$(5X8^w!yBz@f z$0s?QqKl7mVx-R~1hP-ZW^@(PBN5y)eRXG;(1F#BP}I9T;bXsT;`A&GGr_S4rfx1A za>hWBeSEI}@VQeDQo4wOH^|^iB$St_t!(vS{1z!)@OgZY)=*Lx&|6FFXJbA@6s_6~ zt6PwX#}>}wos(PgviJ{GF_rdW1}VS@^9^AjA@7)$P&r!lL9UPDu`=`q8OG@W)N(tV z=xCyi+b93jJ;&wirPrAuKH#BSTbENe95*->yyA;N56fa467121;ghdlpRrnAjo@0F zdR7%5xhrWFl+Vz4F1$JN{XHO@9Rkx zjuA5bKJHXeJa6yeXjU-DO%SaWOc4cjd^XpA>SDH0Nr^lM$Qk++|Bml8yqB+a-$61Z{Vj_hbBl z&PH73Hac)Me6@KM++Bm&%_uE?PyyF3`UoUBu#*>(bSUWJw}w>y^gOa$Ey1R5^>9C_ z(K)S!SG_bEb=&f-8(Bx_ku52Cv zbrUpXs|f|9v%%Pm3yH5|Du9$kIswc9XI!rPNGg4&F0VaUn4Rr~o9>qF#d3j4pOza# zYeYKo{*J~UTpG*K$W_nPBIU6D%bnO%z99T}`#4DAKrK|2qqD>cg@m=+@kC2LEWGoF z16nXN*xnpFP0x~!+TfH4FUQgz9|)DC|Fl8C8ML`W(pxJQcRAGJDl=A>Iy4=EteIUr zM@;h&`g(V+aE~PyF=G!X9nWl8EHq|$MBUG&;3Jh# zC}LpYjDxTn;z(z_FXWHEJ%`ly&i++KTPziqy|}wdI})Px9m9{APJy(b=GPxXk1Y@! znsolb;Gw!CegAelAUsG7_nSF(_{5EjmmPmpmHpS*u3%?NkxC5_R1Qd@*4U}>{YWLH zkBTT*XIxmi`d*+)je;iL2vYH?$nOd~C(@jI!QBzQ@8;${1HR85lRR`; zZ~km_M$ml?^QZmnZ^xNDURY{bI(SB)ik+uX$ix402vJiW@ja_vgiY{Ij51%jXiT< zoS#P3(G7sUCE7%_t2vsLap%Pweed*bqJULlBLX@}%{24`@7(IdSlIuD$E)4)TMY^=pAK`RPX&^{>Z2n_o)L@Ck-JAQtMt0%b;-TqH{Bb0t z>EA-ant%l*T!_tU>DzT7@%wOP1q!jcAw@Pi><0O);Hiw8>zYRcE&RUZ1gqbv79=yK> z1Z;+b z#gNwFC%T>7lFXIe;m&SnI4;QRoM92+OXNMvOXdXu`gNb|38pWyy$;} ze|iv5*_+vx0FiQ&5~DveE7a7B_2MYlPuaW?UXuj(;1dh9tgx~+6!KzBDO-Ctoj$-; zA*zOn7Q?8#B{?l8-uP=Ob>V4*!NaWswIP^FaptIiBG5*oF)IRiw%T)}yLDDz?k4WL zS3<9LK9JX(aH`LJUB7c%+Bq06NhEP>ySV-+%)HMJUsQI+Kfk1xG+sG%^-XU&yKaz) z1qgrja zpa4TayuT;3qU!~F>PlrJFUQLX9h3#H7)(5G67rL~y7Ha6RrwkN*b#aKyPQQBvzDIH zCxFRIFmz>JuLU<^puEg~73KoY?GTJU#-)#n6Lqg0kbOr!CIGu<5%CkWCb#jEpov*0 z4}dh$(7yl#GWRmm=P?T-A{K@zrYK(KvCX7cucs;=2W965S^Ahg_d05jJ8WbDSdooE z3?`_B8*l=e>g?o=-+cdLi~{>o#=k$(y!!PP`#*<|Y!)mDy@a%<6zr?}NmQ&Pv~hgq z()S@hoM-@XXd6|xvAn@c*&wSV$iIkfPOe3QBq3_9m*%TOLhA#Z7@BO!{3jNH;YQ=g zIkq{&O1Irf1GN_ASXt3%o=L|CNR@g0j+{D}mo#-pi1wfPnpss2VFzL)PjJE* zq_jv`4Y93h(+#PlJh$e~;xafL#irrY=vUq4%ZlGSNS=QOiR<;us()4m?_w25A(?*! zcK^EP{a4e%8~CYy;B4RAy9=RmblyOpvlV%I6iY(viZJk8^2UQP(W3IBFF*+>->)zV zMQjsNeNf4+{k(Z3L+o*m?0dN|>6JzYt7lE=*8o=3JH#c)dz-sGCBViU$(i=d?U-vI zBcR*B*@5|C!1W$h&W>F>_O4Fq66cAyh8$#0#WCpeJ(+FOw*41+!eJCv;rT;JLk@hk zFVqj4o6FoSd$T0OE<)#{AAqQpvK#4~Z}v%cK$@OBIII0+VeyV=LpOK-aGLbvs|?q) z@>@=k+;k=(3HMwa7-uQ~%&S z+x5xr9f!WUbPm&Uh!P`Yby{uQPH8%-KYc?9^+>=n?EWFTl39!YGU4IV`iy{-lypM3 zHw&<+Vmc0!)I@J&uJ(qx@M8f3TFaLOLLyDAMu|`o&i*G(YV?0&X4XZ1l8fT=el?3u z%CS~!ay5Jy!nJwlPRD!VuiZJLYU1_;+$bUN@z0OI%*-;q&c@t6O~nWO`3#<|5@JN# z%>i;Nb#k7H_U!^T>MAM^jxMI2pV$?%V?ZHp1Srr_QE2O!h)ZgBT29-up|P$@SP}T= zEwfN^jgVI-KPBny{mykru8rVnx+suWLL=@N|92MuS)1JoM3eg(7G$sAj!t)<*(lGL z8DF2&pvB0;XtXhSpufy;&{FfVuL8qnF0xG5iug+HI;lE4>DWp$)UP26>LzvAE>e-~ z*bC(NTq|NX);$C;dZiC|t;nvT4f50OMD|K2U-ezC1(fJk#emqTd9@GC6oq7QQ87D^ zO+(%i;)`ORzv`0|3{RMDA|LWY;FjMjtF%mK;Nv#omeU?Al|SOfQR!ERC+~Ooh3Z2kwb8Y{^pbIR-~=O(fNI-bFQ|A%46$QywolxWl=p1IX_HYbhYMXo}j<4Q6e0Y zQy|uq=~}vyZ@R1zIjuWu8<1W^gst$RS8LCfE^z`am9HIfE^$&z*+!<>-b&Y484L<( zmPZKbHBerYzDpT)5g>BV)Po(vipcbq`8XK?uTSwM3Kv-`jKv+G7|@tk5J>RpeZ8HEUzr2+hy|K zuXk{3{K{cdFGXfqW{9Q*2eV+|-IvwU({O{33VNDz1P$9k2M*+}m()o+Y0w zFU%~7>NuCFmDfa7rnbdb+gm1TkTgu-HUNVhK3V;rXE7#j0scC7Lk)J9EA#jR&^&Oa z1S0M*`gzV40XBEHd`hKUbSm@vOE?{xCC04Iuv63FF0kp_n07Pxd$4aK;w72w#;`j8 zXly@D=PsRQDlB~25z3c0oJ0;TP21$Bu(oiX$&Dk56(VY0<_F_83&irS5dk2OGaN@u z!Is;PW{>=g2{}hBD%sCr78i)%z%Vv@{9@g0>sP|PKWNdL>8FGzrUb9|&r>J%23tM9 zp&0EsI-8O-cT5zXHP(ac?Gu^mcs4Yr%@sRQaVcn?Ln|fA+gaa{X%5 zHMJT?GLhI>(%FE#Vu37;?eHj9O#`+r{rRhME|WhhXLoS&W$Jpn0%Pf+9H_#(5p&RC z$mOE!3^3o;<$zpFfbv9tkh zGC}3xoT@NScK=dd96?2oaY)n%e*p!W7wghJq7{&9$ax; z-#{Y-OQmFeARt`v$Z)P(?6i>8F6XpN2+1(T=iDaeWEFhikyZ1IF{qwhp9;+|V-f_X zvqu3FTfoUUIjKukh|@4*_@$C$Ujn+OAGvgImB2RN7WyAfQQ83~H3V0R!2Wj+C#DZz z!c$;YPxNNrhU=oEYJU1S`ED-fi^841UIQkZTKF8JoOaAEfHYs8j6`hgo-jUrl1p>G z2*t7Gq|!!fyr^;%qO%K+b(yy;F-zwIPnfj7pjarYQBqko8>xqZh=3-S zXEVSlYnu5{V+%ZnYN}C(Mu%t1jE!REy0}WvJPKU(Rv2hC1(w;?Id9jXHE=4z;RD+Ejs|JA$$Y7e~~KLIH{tr zyMkPnBh!D=S9m3;jZ3G;=Cw^>O@#FA!<d0F$U5%wJuN7xquD62q_fI88n<8k*lE|Q(S004OK+S{X+j^{#BN<#Jzw3!d07G- zIeg!;3)Vd9&Wl9W0L2*TWq#dMN`ELsF9)_1>sB+8y`6~iZH(C6VhBhb`1;sQxg*E_ z3;0961^Z7F|77zsc%P=veA_X)ge+4v%w{~A}7<@B+i_`9_x!R z=js64j&LCbMNxAVG+tDQm*v9$6hDbKR{L+!bt%^atR(b4(wJh>a_@y11oH00lVbUZ6B0vP4i!ENo^q&_GUU24S$o2&E+CJltyNR2*% zF=SwI=k|;vlD3Un2goGgdFs;WD~?r6$*-NlW~kYP_q}vgVL_)jljBt$Jr6rC-p?@{ z%3Y2gf^_csn(25BZ>aP?irDyX!`I$tZvq84PZ2c(AMg;cF>=gh2Mo6Qa?CBkb0PXT zXcU*K_5i@35hzq68s+zDTomm-p>FOpU5 zqGtN`;YndoQGVfKdS1$M6)i%zzDJ@?Vn_CiD8sd0IYkD-XX;$?3y59A3NXPf9?t`z zg^8A9>{QQgk2C(2;`yu@VNpJT7-r#+*g_^^N&!~SKzIo4Ac z;v+S#zF5Oal3t=sJ=OXM0f?=c7|S18u|A`hgn>d)v)RDuqhp|w2c?ou#GrV^c=BPM z!7}58?5VLO0OV%XQE^^mE})}b2;&nTGeL$cy{~c*yKR>vj|m`E>*6Q%+i_?0*53F< z_Wji};T1Rt+;ctJxYzddsnfuhFBZ+%xEf_VQ24!1pJV)0rhK3_G*KSsgMtqmi0pL_ zQDW#pq%w$bY&GOw$^4gMD+_NoD9KdBCb`1eTYmJYh80VofFs^DT3hIQUQ&fZ=WC=Z zZ4*`Y3wIbfkbVpq8O<~rJ~$#Oy^ED$&^N%rxg>nB-m1IMhx$Kw6JFRhKG*uoRJ!L1 z0kj^2x4P0-8Tn!J9F~ntEq=vp_Zb+2zkWzs3t5Gy0v@P2m~zMOLZrnW9Qq7fmLVFD zxL*ZXxrMFOmd!TsYYYQwE&-!>3;#$rccOX$R*Cp|2m?bJuPSe4ddpp+1(LgeQMduk z=?Yw|)ne82W`bIK&QykaOUbH(;`QB#jP;P5o7*bK>OO&V-xPPkYRhe>+@_8kg%E#- z1%5o3|BpFD6FB-p&IFk}3wa>lvz(v3^>Rme!n>w${AmF`3_|G&&FwQTyx*2uE?MVV zp=x<5Vh;U1{%ME5ZcNY2hR6}%tZOGKvEtZeElS&w@QYcKaAN(!$NUxFW~h8eaO6am zg8E=2U)gy55gm^kM~B%aFVzWzw@oTyCuI6RXlLPUYPC)mlv&6LFnRfbXb|7OdQl)m#`8qfsVOH^`tW;Ng{w zFg*{&C6k_kWi%J$@kl&P9+07d`^}sw=NV+ca=mb^ikW3KK$r4MJT?Gb?R^Nq%9%k1 zmY2LJI{R4w#0E{=xI>fJ+(XUoRuNr039f2GL}}W){lf8njSmPQ8ov@N8Aq0d{!mDA zEqW^lEK=%x`0`9(3q(+cynd~&`|khwNM#4~5((GE@yS10=MvU%Y#-XgsOO=z*sK3; z^dh&L@o*4Lx-R$sUS|MNc8wgRT!GZD#dI?B(BZy&R0CRAATmb|a=+T%b@4uqX=|bq zW~!I#Yb&*T@B|$D{pQ-zr(KaxooJq4`O)ettldyb0C-&b9kQGFw!>*;l^?f!=XLqB zlV0NAluzY8n};{v1EKHfycrc^WC*@FJ^=QsPYlIIK7dw+K7iw=fvEWih%4BzAX~Vi zG+iq%|3&5uhPy73vLyx~Yl}DdYQ0^pJWn9dDTID-Cev*P8?a5k0JON`Yi- zDvA$Q&iu)A$pxGZ%!Nvqjz~l&^ycCtdI|sNe=|70`<3~ruG8vS-^6EO0`Y}6cxK0E z+M`3Ps;Dk|-9q1EDU^~W-qyHH&|ru5rMx9im2J}C(~$Td*C+|2b1<7g=t6FU7P+fu ze?E3vbZ@Tx?De~-M_X08#2?OpVWfL$SypVnnn;@g>8t<1=q*eYzo0FjSlS-t!Bm2z zS(fOZC!D#rz~47DGT6gl`Zg&hfN`9!Gt7jS#ot*WWD6`dCjW1L-ju$49h-N`G&L}H zbr8oXkS$KWYYZg3QkxjU?_Xe=BkxAkU*wta`{A!fU_aP1-O@oM+;Q#e9P~3b-IxDU z9u8QGm7$%2RQG&?i7ZIM31hs#>P@}g>`F@WJGUxybMdeo8Y z_!t=6G28fK$Wk;x_{MaLQlUVOTXllnd>7-XytU>ERGT$+wzzuD#u*8L1*7OmFL7N* zR9y#JoT#2w7k8#oJKV>d&9p?@g2)9l1gi1vVxCnXZn4}Lwym<9GAjb~aVK|kvHrZ0 zRLoBJkaX)ShM1)q=fPc+i<|`4G~Qn@w^3Z~H-mKY-z6o*UB@*p@j_Cc^$aNe(06As z_gaVI54m$b4w|^f7#b~Jia|9rG-Z9U9rRx->I2`J&2*neEP4%sic!^Q5F9qIEy_z= zOtWn9)FT-dIxPYNofu~yw-UOa!#o?)gd^WvPp$574=XVEnM_=}BtVYTvS=G+3+}oy zCl#Uh(+%kixi*SWjhdWhr0mm8>KpKoFk*$1{d}%)tX`6!Z72tOk55Kvi(sMIOIk~f zV~Z5-iQ{t?Wz zVqIOJGmn%qw}JP6YwXuuBP_;PeDYRr@~(hks&rH>VY zGE%?k3TxC8z_fKq_MJOVhk9m0-hrP#b|Q5_lO!YS84sH9@5`?xyt#<-EI}5Z2F5uQ zbNIBsB+_d17DkO!w6s1SYll8JNizT7p@#~^pNv9T^);V2DXvxmDXYz`cN@)dwYnnE zzHG_)eu^86!svUIrwfa^4QL$~2QT23wNnZJ1lgApZb(xl5K$pKuyxQ(u5MLH(~pC$ zZB`V5x^n{S-nFw$`fmSHj!!w`r3_G;vB1UAYxoAmHkzeM;xY8FkHz;<_^R`je)b^G zK|LK+C#t`br^zryaL5$NGzIGyWSF~;&$wSh{#wk~`MKE27s3$M0 zCN|fOt?Oz$VTPKaTbHXWzo9xy+TZMqkfdItlwKD0+O@AA*pW;h_|sdN4gMCgJKN8$ zfH9P!8SQ01t_y_jsrW{cF>u#iWBzgoy6?v+xRdZYze4SAYvCftZkI3;uHqn&^nxM9 zMMOZmxQ1+yRoS2{h@Av}>{J#gpA#*Pi1k*~V%O*aqLqp#115oP_~?*>85q+xqi1{H z=NVwKWvRK@$T}o1UjNsAsl@zsZl*{_C9lrEmjEl3(jneJ+hiQx_A%68T#CtBmxxh3 z+&Ko5TSz!z|F{^V8Df~a0)T&2$(c3t5q;(aS^5YbMh5qQ<)-N!q%m*=qqJY%$wt7F zjvO7Wr`VzKJ^>qcvd`N;6;#-g@%Ro+}jJOnBSk5NTDQU#z(Q=#Z9!xt|h%jxF}h^jQVnnVCvZ% z$kr}u!Y$HT>*N6LD5?)|nV~JP2L1LLz(4eaMLw71Oq$28M{F?g=@^c@x75dwN7aWM z(!k$x_|2Zl?x&|Ta?I}s-2!4N5;Bhov10zJ^cr;6{`7$*0olzQ!Y`0ymMPy;|v)MW=r zY0!#~@bB(LU#5Lj8)`gmjR<2Mpy37$#f=f^N?x;SWUgT=&tLL=Fv24QypwZXF2 zapwT>~PHm9J0HGoZadkyMVZ=345FS|_B$GrK`bh`5ri!@dFwgF@?u|)VT!i>a z$o^ZsxiX98?D-}Gb+~s5%!a#2@Glxxa-GWHaEVwFG9)j~1u$UkxpR6|Hy(}hod;Zp zpwy`gi4`I8FLK@gwt6$Z4LqPd2e~4yRwpq5#d%d71Ierl)$?0Ifxb1R($C+9* z5M$KJIs{O?F9x&kV6h5yE_V1L+dXucJAU`JXB5?{@l&$sH3kU`&kH0++>h-_v}SOL zhWnP~h)TRY=@uplPQEysFmeMscgF6a$-6fZRQcsNLM~T(FnFTKKHI_faRc{udyOI6 z+?jes6sP`G`9HWpel0wP5Jrl#u$YCGD~;BOPo$s|_+14@G9g;|8=h)=%&{@sEr z@&KC!v2e9yu+ovBz;>3x!mUkD36mS-pYBO>^&6015#Iq@}?iC?E-?8Rw^dg1|hw6%WFEYpvK)kdi>75jHxl{5PJ5|BQ2B#pMYYGluM8 z=e=-;TD8l|e@?28R?X3CXe!ad=_`pf!frFXMcdr5*2|I})W}4l!f^WZN7z%-7|ao* zp=JTLL8O-C<1T!=z6WwZ2r$(MNbeQuzis5nPdjOF3rfDh{u1n2wZAzecik0_6zMd} z6CfWC!3gq>BxM${2$x9Mx@oIp;$1Ch_kp%771e6PqeZ*5r5?|LT+*p_b6NhBu4!^F z5$*!8U`F$IFG1?q-HjKeo%q-#ggm2j6odO!%B9Krs|eb_KS;i+eIH0@$_M37C&!fe z=`3vX=}E(T<$??l;y#J*Q$_hv>Xs!*%hl7Vwz}Xg@mY-^{F34qs&M})2Kkb38fVK;^(MC%W`rpY6mpZ2@qbYTHTQg*iLtbm&OQO59!lQO(%RD z1Ru;wzkJ~nP#@g@wtZ0STu*(sb6*iOacL_-Hu@TU37x31PN0do$3FFZ7aeIaO-)mLBjkD31IoQp z7zV^L4Ir%Inx-e~Qvr=QL_j|4r*W}14114qPMv^X$;|6;ycO4r;6kPeLz zR=y)s@BjHvYy=iY_jAL;X%30QFK?Tv+cZ0;kF6GHWHNKjNra-R*yXN;Cq;2{1}fPQ z=(=q~@9ZTEI6)Q1lq4pIqjR?m4hW4!IM>t9U*2DaLp5D%#?B=NvvB{)v}ir5wA`41 zxL{)0xIGcMJtfC)?+%lNg{s?j_$9fIk^jy{Qg(9@s753a{W--}AhER=*4pzd9M))o z6GqxEXPEP-af*_cC+za3=il|%PMvk2i|(@o=c~b9X5>m;IR-f&m!Og5)!|vS*Ddl; z1Z{)up~c3(y4|^rSoj{b{}>cl9=lrxn@e1O&2A@CW$23+XeNOOU*g8{-6q$)ZO*fk z?m}UqX6e}$6lN#4g!wf>GIy9gdnqcu;q76O|K<6B$*S->Ta$JMqU8E1N@U%5EJ1sQiOM!HJ`IzO` zyS#d3vm*Q{smxUGKq?l$#F6KR!&V3Bpp$X<`3}rupkLv>FZc$U?@yNoSVND*LjB)h zzjggh#fFi*OW95touKLKsGXBbJL&BGP_0G~4FszJUAM{CLGFzh5Y`wRhD%X-B0=nrX=T%z!y1z6o_=vl1?j=5JK1&Nf5sW&2 z;DEBLGN|#zky^h6xE0Meyfy+qNEHF$ljU4`{kKONvJ>>JGXO^rDZ2Cx{yI5HH5&%f zUC3tQuv-t$KT^oL{;`xW*G6pNRV?{FiccqCAb`(J#O-ZPKqvZ(Zjl$kzPf+hmU|9> z7H7%3R%W64X(h4I{<&@?fl`b&l1C5 zz|l<|t)S%1$j-43Q+{OG=y}L}KtafM=@fb2T~~^nZMHZG<$-SwCqZ#2A8q7urs65E z2R?Pu^Zt?tx9WG8K}$2p8PvFmMXJ2xHa|Q@vSm^X<#ULPAKybx@K1&N8t3D~8=FNV zPFm@<LY@=GMO)0T=j5RPq?O79;?b8G) zo$?{qC3_fDi>(b&20lJ@;>6vgQluRc8$Sd`5jp*ZA&0)W4nP8f*$j-yd0s=5ms4m4 zO8n|bG$_vwL`N0MJ#Vss>N6j6=%YS41&gcjF~+_3!iEl1^(?1TN~jck@wDnbLg{mKb(; zcOWh;Tt+ycRRkA)`f7y67+F0l`HfIkz;H^IcBpUgOqa%c-7jildath&T+XlWo+yU1 zq(NUhYet#vueiTD+KZH#$uQ<^AFHr87TCx2N0P zaL`@X9UWOwpP4zWCr+^AG+aWDI4B*YTMF>+fzkLWb>YuP*gHW~*AF`h}ig z5HR>xw9tEu!&NDUrxMNzCi&~H#kA7zX8j*DkGbFZa=9VtGuF`Z6vf71r)vKuI(H9B zra0`^U-Sex&G4>R(ANQD55glR9ilzJt#)mw6jaw0lmp%X$2W3w(gKSAbCWanv**K- zb^7;F7>w@*Hy24#^h3r%84dD7eHd5Xj&%t=9vJiw-Co6?DY&-oFSiWM8kQ2(v@6su zO?-Iy>0p_I6k17^k0Qu6eVFzT*}j5x8WDo){Ztx}+Qkkx2#AtFu7`Z_B<42d|6H{s z{q6b!s_`Zy*4BQC5o(H><6vYOP=?-ndd3i_%L?$U3%{|;85OssIVTZgc~0R;o-wM0 zJ)1aBJ^21vwVfm>@yr5(r)#G0rnEY#>X|Q{fa9M*re*vK$9q?Qq&KRmDC&IhLWmn! zz@e>IuBZ^E6J-Z5rbdh(3a>^d;msx;HBM%ix=SMso4vec8%QOK)d7`wuecpCPn^jq zxCZqVTs(>Y`1Z8(v+ADiJi5b%x^esSzD39*&GnYXVMD|R@{tWEw)wk}R|A4RLg0Sw zLp}wZsD&{a1>TvrSfP{016Zu=9c8qUdFvPE@MPR=f7ys?nh5+VygShS@i5nVmPJ;9 z7piqi4mns#6C5QCWY935x+O@IXV`=%;Kx9)?P$9pnIzUsMBe{C+nFOg z!oXlHN~V8{{St8STCY?_-dLvwxX~* ziss=|F6lvD)7dP%*|vrMZOr(HnHK~f+es}@*K2RM_Xi0~Hl|o+KQgD>0XC_<*h@)# z{QckCpQ*9qHhqp3&lu(()PB=3f%&3FPtJ^R&AnT$YmE^%xRRn{uCANkN@0p3>)OTM zvnhX+(?AZn@3?m~I4zHmwni>AeZZ*oyYuF3_FnGeP%|3IRT!fKiEq2^ew@7-5cGd} z`k3G0d3r6d-p^xFl-ird;=3SY*i#%yamqw>RqoNS?p6P52F6qD41-kH!#L(oRQe6FmF!xWZH^#yexTRB>z>lNB)j=mJx=uFi7(wpySJc^m`ni8}-7GP!OXXKL!@BS41ABp7}) z*79?^(v@M+sA)8FhT4maE5nO-$Z)xH$b2eIswOcC_+X^w*I5NdRHNEur#H=-PR04%2AKuTf}DV*JzgqvAAdGW zIDA(`)#fYvIIhVBK!Ap6c}~^~(X-v1_k^}9$0=mX}MU+ZACHD{JaI3OX^EJ(D-6gI5U6^jn3gKrcw)sdoSI4!+ z4hBw(WHRPsN|yF%2I2oroc^3`K+#5JRCNpJU}p@b`IKb&iKeU*!`t4#G&^XYIpk)7 zy%p0^Ay87uV<;_T6%VJfVJ1OYXos7=fYwDHbF$zwQDt3WMnvM}r|Yq`HqSq-)O<1{ zVfMvu#OI?s(J=}TFcoT-eSqIvyOz#0U0Pf#M@_7|CKbMeuTEVjgB1n=O!`#0JWAiH zh`zG_RF;h&%Izc#FT6^l$L^Is%-m%AtMDTvXdkiTBOLJ9L({9c1Xgq_&>$VS9Teo2 zp}o5Law@30g3yVEW2NqW66G2b89KpdeR4rP^nxyv(KB4}53#8;sHmUybL_Vk?6^&`~ z*a9WGEZ6QZ2rke{v>zjO57EMLT?vQ;^y-SMa(jDrBoFzF=?P2b+WLYvJVE8Z5o^m< zxMSK6>Iag)5{|~r@?;VnG3yjmOrl^hYgy21q-lRsVC>rAn=6 z`;~F#+}F@44jN~I8Gxy8K)F+5;Py#oMu+d?rIuFHb-N(KT&1j>ySzm^68kf*>+#7v z_c@BempShHf=_m$RP)V?(m`&=Vm-pJEw;U&`?v+X&%3LLtb z+zdz=W*p{G{yXCEJB>R;b5;3}LN@}h>=C1M=|f95Y2* z%D#SnPh!5;48(DBtl}UZw+nbJ^Om6sAGmt1n&H9U=DTjnulY0_wt)I}u zPC~gW9&-Hf`8Jd{bAEm&Z+ExH`c`a13Dx=Vm1u`<;x3e$vc!vz8SR#WkvS6d9##@K z#(4gV4_d#3pS#-PhJq}m^q_{(@|9v7B#P&6x-F>>7rP=q(4ur&{q@TzO>xE~s9+cHwiE8u-5>dk2Mcb<8A1I5wFOJD8z~e$=d% zp#VZ40K27@fDzxQ8=J17yT9~N6U&+rFza>gVmY^<*do)q_^<$6mCOe}h$(kfZy{uJ zt4WOa7q0xVbjC{l1dEpBujl)Ck;P>i|M7tgz^}(}Uzd5Q8Igw9fR-}!)I#cuzDhzN z*m6ls>~{Gk0CSEsybvL{y2}f0rTI#+{mV;w;_C{d!10t1c1AVhhXzu2uyIKR&$A>o z1v+}#;O#?g1D@0ZAz#+L6)l0f0=$t&Gbm7mCja8?dzA?OWFZM(%+3tFWN$H0fx>r+owa46+&LO z9P@1lY+*5<*<}-nGbJF%kxC-bneyw|=Rnohva;s@u^eff99D_5xu)D&om(9+?UWE ze}+AuK!B%Uq>tW_t8>y0&y6FTc~Sd~0*fA3AG9h7j+y&e6;9}vZpkjbyFvE_zn4XIS5Em%P^n4~+-HVG(g z=Rn{kniBl3;Ei@-`mlAg#`6Y711k_Op<6~=)&z8cQ%9z2mD}yri++-lxB*jS9inpG1c64D>J#KhPHMdTPiymqk=CBO z?W`l0kgocD(kKjvzRN7Dw|F$=UvAC+K3{)ZE*)gu;C`EMHZ^xTk z<8ID1w%aJj%T!nY9?xubs%8<2-y|(>_=sTzS2|wzGvqnZ#=i=p{+SdENj()sGp!fs z;;T7uZ$Fpd?;^gfW(N?(c{VE=lc*QlL{DqDN66t#4s)xw9~)F&2G;=c>LppzT+-Nc zMJVi27YDI8c;I&6yE4Sm$Nd1zw!&hLXVFb7mmI?iT;MWn!9jExnZAapT;Bq*r?KdB5? zmY?z}hodXrJ^t;cjdDVKY>l~cb}&{gg7bdsnls`E2Y;Ea;Ze&R*HbHR=u^S%-oMQ( zehX2{r|>)$KqX)d#>r~JRnZzz!b3g8yJ^I(vV0-Xp!O*w{1ed4n!&AN)i?MPjjJ1AQKfm$ui3r#Rrb|%0>|ma=50~8duJ_X2#e$yr1xIY~ zu$)i-e5k*Cu*U`iqM2G7D^9xjo5>^6aW9r(?NhiT(d5GGl-n6d?Kst%V_~bF&WW+V zO}&J`50nSEssv@XXd%_*nVdbtHp;tK$g9yDwnhj8^x-xu%@A*UX2q&RbvpM)EemRv)q9$|UiIh4NTS0k1A5#FVD& zPGm!y|Ei5kZ?58to_*xU04_XY%M_#zJudxc|(8@0Vq(o`3%W*VFeIz zXJ~M@u~b4Ds*oG?(d!aF-~^|~4ZVAm)bed`<7|1~$h)5d$8u^I4Q;XL>QlaeqS+{h z;YudwbzZQ2H7A#e{+)N{ix0I8U?8~ZueEtw;;gnlA;JUU z&cJQdi(TV7v&x`4_hv4Fz%7MbQqsug@#Hz#IgtO_FmKXSHB4$pFM;&5WF_HCR;`Hg zfU5@JKh?aN>24^ptVDU&Jz<2Ddvrti@PVTxdeg>YmroKZ&5E@6!CXil9Ve=PbD5W4 zL>0rkxaZqS)k#chsM7qkXxM}%j~9jmAuDSggV=B}3uaZVmPZdmreD6RR-lz*#7}>M z|4k~2nBJ1dzrcweXhx*}BbXCEv?&YTIQbNoiHaH&f( z6naAZ^oAQ5^rNgrKBG6kI1NHqChW!UNCHu?%+iM^kB*k4^c1%LOHyeOgV;Q63MSQV zz=%g=5%D)PQ|e@!LaGN$LIZgrCOxjrhj^74g95lxOqD#?X5w+Sjufr=Mt4E;F7qk= z!_2%H***2umS@_b>I#;9u9y$G&}Y3bjZt5~vm2}bs9N(iGYZNC^J|$WJgz0nIhzvj zFuBj+{up|P%!@)%0QskiUP{F043Vm+EoogP9E>JA`*61+YbA}&0TD@vsRi+?L3NdE z@t~Fr2Nj0c5iq+bwNw1E8Pk!tKY0 zoX(>XQwWfWT=t059tRc+Lfa8`y`psI!-CHbEei*nh5Tj^Q~uUPJ!t@O2S=YqU>XVW z5CC}d>rGkwt57Oc3pRZb#iE};>=5viZ=f6QU?1Bw&b)>YB@ply`X%|#-d+Olakt3s z5W1Aqsu$Ldgqu+ayfG_nfxRj$1$LsK1LMkm1)@2}feqUmmm&>;#GO1;0ukpwvdnc+ zn5`@P-hZG-(CaB|oQL*_1_%v{6)KxJGt{-31s>;_rEvLQyZ!7z2@XK(=q1vC6Gr)> zn;lN>im>1Cyb|0TDI^k=8L^vDNktQjrAbWGt1?F`m1fw!z@uiQ*m|8gG?)fd-O)1a&kqd;188cucjJdQ`}NAd^osAPy7^NFcE6Rc9Qp zgnc+o+AyIsdBupzgkR!>kLR)Qjcz@;S~ysWx$nfKvqxYAxsy$h^$$q+Wf#7#S2p~C z93g?(8eJh}<6NVbWm{*Pt}#YmPSeY?3zY?l1n>dD`RoMr>6;*u7oOFVZ(7^O{$Y^* z?uK|?()SQh!0Hk-6-)ae+Q^Sm*Yl7K`GS^`J72v%zx;&bzFZ53M)@Mp+GE+cb{@&WeZ1Fvxs5IhYvhkWf)Z(?gbzNZF1wY9pcU>cSu8IOB#KA>2;ZgH5{nF?JOV$Z{#PEIW{A>bs$4+Ys00j zGO{6tw@%8kd%t@7hbJbym--GiDg%+?O#hcAT2+xXb6rfzWh2g4mjZ(@WT9%$Z#5^B zH)yoAj52wTfA6iC0kH^qnkJ}nH$K>M$vtTwZ@bzM29$G01bWLjjQc_DLS&dyBTM^! zN$U-(w``RRtQ2WHRPkOO?xG#})mbk=uBcNfSac2bG;R8Qfc6pWA9 zoJJd$`Y>IFRFhKcJtqP_v>X@-yla8)85BEe6iy{%K;DL;jnVpYJnma+e!($>iW(vrJi3$kt1l3QYEXZ7|Y^Q1B-KOgXHH|`pnzO;yO55)nTd}&rivP=@zUCD7B9&ieYO{CoG=;-Uit1o z0=_v4B7M=Hn7OYrhD;6;Tk;OC=(H&*;mj7F61+Z8T)d1aHpHGbkA6cUrg%ETPm zh*er5uI^c}GmWckc4;h~tQJ0lPl4Ou(|S(@;4{@_rrD;_Csd7D7cdy<>bPeMLn}~V zr!cUrq>29rauQ|zE@F{1Am|2e6I`oAd*TAIB-t6qoOT1Vj2wUH94k1Gu#_#5y$6e7 zO;2$%y8Qs}Z~K&AWo7FO$%ye8=h@N&x)ld6?v(R0z>sq1=Xs1 z6RKTcr9!B{t{?YOB1)mQHK=f_ks4o&ioG8+S(#urs6A8gVBl;yf1=LDL9GrLg-~XL z$VLKuB>WF-y0v^CtI3_bLp71jM))ah(U!Gq|ME%U)3S_ZDxXS_Zo;AmQ&s)m|tPax0-*?jGs5BC0 z&ATxBeW#51*B?qw876O1dK65 z{BdcDe%a1Ir8UL>8;s1m1itRcnhhTW->_EGK3%7@HzA5gLGC4_*Y-i-|3f|#^@Dy- z#_d<^B$^IO&ZZhQdfulFr8>D^S>Z@dN{h=-5qSM%lH_8XievrOYHhwRHefHT;Aq@F zrz%_P&fcC1HWYll@RXfN(hy4h&!qchVsp|kEfJ>W{2p!^Elamt`T>;6)5LQC-qW{y z99y#1wxs&=_sou;K07<$F@%=^ZLY${5+(P9lEz`u^1qjKEB8>_k~WW6!sfkNQ2nQM z?O2)uZJB|IPF=~yCe&cXvYQDC>~Fyf-yyZhW2<7Q!YO5B6$^GBi~ zZW88u<-DYy_fL;P?Q1inr>R3TStzzlA?(#np#&hVc+GIL@N0N&t}pnj!KLZP5<77` z_J=8^J%^wOkEp{%HJNyiN@*u1uot&XjOh~7QMNOeJq{9QL!Zsdcd~yg(;(?uqwcaw zAl^4S=>UoBR^vRwlqc|cdOm0WRSvHI8GkGv<|Me|{E4ki1ZhrsX(o~nRQsZ|1?~1= z=S*BU{_`t7*J3Mx>mf8Q4x5I3VFvik`@H_6wI=BS%0UF0y4~uq7MJh@t;JVmnDV88 z;=gW>A~yYXVf?m}`lePbLQwF*WqZ90VMSo5Dp&%dX1jaEKVpultWR~|n%>F78HXE> zvGs6Oxn&i#iKaXE2+>~mPF)Jl*R%2o|0ZN2MkR6AcU%4E&aGS{_A*)V)yB4^Q3pdQ zPI*me{|!AX^#!2z!OUpED|D8~)swwdgvzL3OdV3GIYF2dBK;Ew1DkU zb5J*^9d|7C(UqD?h{qR*X4)gpewF&4c1lJ$75W&T;HBnEGCUV@Mvqy$U=XFG9Myu& z>d!2(u)j}iXm{nQr$14v{5g-h#N599CYP zrSD3dx%nQ1f;eel>FR#09Z!KXq#yG*G?1UAE}!h7FxY5DOAqu0DQb1IY#PK=2{Wfy z_}q!owy9Lgmf}diRI7IC{3iq)%HvWbw@1GZkG#iX@Zz`3D3{?$o*HyvJ)a|is%tUO zj?)CXiEP}%8#oEiJ7 z%zk~6BJMrbz$7A})Ikxk<7bB$PoIllWu6f_i~HD(!K+MGctbipU!IfzeY&UUZ{x5cK<+ny=e zfo14wikD-!$Qh@l9F3IVeX%6o%(0B(x-ClBa$hS@5lm+0bM3)sc-^chxEalXF61Du zOR|(?w5Y>_eD7R8K{$@?d|JAIHv*wC;}~~PgwOHj?Sohx68w*RXD>NoA>qi~o}AJE7a*L~`q9x7F5j-uf$ z%I>%h|9irMt6TUk3xs^|1wAhk0204cY5?Eh2O;2MqEJo^)o_lA49`<12*rRuw$6E; z^&kuX#zE4V+kw<^VQom>D`a>bn08A%1c~4If@sc!1~34`WkZ4UPrPvxG!u3M-Eq4= zWT(T>vxg%j9XV6ta65yUhn~$neaAQ~wyld<6$uM7&`A`?*5MAun58Q$)imaF!A~Eh zzCKJ@k8+anbuaY~{tznBUaPq)E)$beQ7Ag61=zy=J`ZtM6fP@&AsJ&E3(W2S2g6$@ z^;E=(`$tK(A?p|LOIUVY#b|v8B%pMtIvZA39gp1kCBx58vdp{10078Fo||d}~7~d}DliKBSFCeqaszr|`si zYGTka&^#SW5h>oK<*?9r!o`I?V!~rIOdw4`o~G)U)?1C(+5DCzqqs4pcd@#!PSZ9#?y|MJto~Q* zaR_-RXL|2`(nUw}^@CGKPh&`C_2Ayar{Yfw&1!4r;SjlIs?j50Eyw#S+e1R$I3>sK z%Tj-}J}``{+B3Gd&BrO_EG+E?8wsX5T;S93{m$3z&n6^5GA!c`M985uetB%3t08{K zGlDor%~(}kS~Gvs(80L=-ruv8mA8fTV$Vtuj$&cY@iH>wOXc6|z#@)pQoBXq$gW5H zvN1LNgu?P0iut!oT)+X0j1e4C&R}2euD43FMO;qW3-MLZ;pxE<^pDDf05=e&R1xT` zHTqfpw9+oxSy-};1Fndzoet!5hB$2>jIW^asOLoI)bSWUg`m;6Jzuc55{Zt;pL$&Y zIGtF*WM99h>_HB%!7$L))OMV53Gc6w-WRmJhRZ7%K@4S@6xfFR;2Xgzz={AgpGcLz zFrODU-p$>lqQ5zpR+>27F?j*Qaa$>?%=jzOn{aomo-U&TZCQ}m6k51T-`8hKx)M%G zGWULWo-p9CAcD#Tzl#~@L-Z)kOod=P0xsw_gQD(^XxL}e^zAs zo7Y%a;uqBPN>JFRJf!WF7)5|q)c}f_NYx5rRW+qvK3yz9>fZOwvY;7vFFGt~?@zi( zd7%2uh-~TgRPY5pJUhI1OKmrMO<1=fSDeHu)^7U4jtH-dUlfAC$GL4{8b7&w2C9>t zNixbpk((`?`jO>==6edOgkO*yXzqLeG} z8J(3Pxx^3MvVNJh{VOmsx ziwWgQyAzyHnyJiUj)F$Ek|Wg;sa8o28Qn3uKM%afS;&4(qD~EKjWJ0JaU14j2JJ^a ztc`Bz;GIe3rdcFM+pKqoHSs3TvLi61>gR55Z7G#m3 zmTyn`xGjLOiycrE!zKn!Bu~5o_WOGm(~6;|_W;$pzd8if{$q_<)bwGwUs`DME{%3K#R(BWo))aKxME|Sfn+i(<7_Ds7r8y1@@8t6ejzaHEfL_3iki?O-ADi zp5+q9VNs%t3hyLi2V6NulJ@{QdN$ky4QLqV;Qtr$C31Vo2ahB*)kJ>`nma?8EhF#D8^*!EPdMzi2EdD$PJYsP#${@DwUM z9Bm+XAAWqSVfS@-Bf6#kW~zr4`CFFNla-Xlp%+3-$)$5G8$03cB@n2o>>_nRcT#Z?nLiA<|3w}hXA{eRt?Q0qJ>h2 z$tstlo`+TXzQVg|w0fTqO&=$Q!e$+F1ms-Xij#rxHQ(?bp(YRMf!%uar8bb~^U-4^ zQnJ5%JN66CH)=tWi5fgbSX2ocz?6t=B-V2K;=Q(}`##v;6ARD6Z)SHqrU{f%Xz>q% z4DR=cL*2IxXCB0L91hf4bZS0-FCNr93JqWa38ho;MO$QNNDECqGtwtN&zDl5&pnfW z^Y+>c7cLkkm_XCo&99_&>jbGPyqFcY7&mkkoIhL+##ud#R@K)jzXlm4DzzIZLSXTg-Zcm61GcICYC&o;gqkdIFf4AAPrsU}gN8%e;1g2l1u3J$m ztgHRYqn#7Mu&t+h(xGlw#hTH}y_dC+TL!DD*Gj%)*ecpxDTf^H&d?{vkXwWbgPq)Z zumjAhFX!iLtjyoJO$vL{4&=?JWA?cDRpaw;%QtMo@5mROiop=f^PfHLMm7N)b`^aD zOD4Ay;X;WMduwsReZ-}hRTyRD51+^ z=j_$YTWa!a`+}HQRi`|7;YB(l!ibnU-6u3!82wN4G?@`@;$7}L<5Gsx;rK`CNr)JR z3v798YkL*Li%yw~{E^2xUp3i@p`o=TnefQPVI;X|2tH|AqX#@A`;gHG>Drvc5mCX4 z?ae+Iu?%KICbg}$KIGJ$t%|DBx$*siQbSUoxYsYqm$jonp%y{($!9w!04?zH&PW!8 zlb2cyrYJur-h{kiqBlSzOCdkZ8qO27?PeCkFWupzPJKtnc^VU`w=oCzyD9ChfDG0P z`F^lR;nn~M`34~1=`&hW5qEY7G>9#gA3HVfbLbERc<9S8&G)Hl=v?=$K3z-Dkkm;F zf1e0^A2och6DyWWO|??8gL`@QU(_#z)l2(4mqDo`AhXqAQZyum(V`zwsTo_|px?=3 zKHMEKjWVpUDcgq<1UE06JJ`VSjYw+}z6_sKJS0BNc^{>fnWU`9drx7iT-h5Rz|s}w8pW1L4C&i?AstNuDEWy^>Qce*lC=^QA!7F2kN{yV zV|EghP1bh3FgWU^tK-RveK?I~L$RjO5)yVqKzn0J zl(1j|iChxaAe{YbxxUf?UJE|+i4AIzbtfE+bKt_p+wX9q^F?B|{@fEth?Is+Crj{7 z$;?mo#OUC#M@^|@w9)!|+T~~{>fahwurl(8&fFUib+Xy7k}!2lMIbCx%p8oEpfVjK zUec!PB9}DuO-D9UZhL{}0K>GS`ulWo-%V`g>5IzjEf5x^?gS3aIJk}GNJLm}!JB{8 z(mxv{>H9imV(YtCR{qSlN!qjMOs~fxzYPDdOhU>yc4|8SMPyLZcR{m=oqt0~Wa}wNnU&W{HpMZMm~B5aUi>?6*!4GLKt=AAE#7r-J_{4g1bqw@ zB|Mo7%X_P>bI0k@h22m3R?}6_=a9eRP#C{CtpRH`$nHEdJo|=S6ac5L37n~U*>etq z&j`IgeZ6gmS3Dqpd|ec#j`U5!{IR5m>geZ6!UKIpxTwRXAxc)zdz`5}xMw=r=m;D# z0^-d{!ie`T>Wk7w*u#&}6;}WFPVq{9@1>A|BF~c>bJO7*|81W5#7A83p82!`9g5!w zWzd@4%S&jLdUIzawG18_kJN1N*w;S&@KmMP_9@L=X*h$`!G1CzLTlr%qp80lk1K|E zZ{kY5sRD8Xh0Tr62xje*+Tp#(7uVAISI>Mp#gT*q0MR-J(cR7QfgS$Inkg?)Z-t=w zzY2(&r9hD4;CjHW1u}1E-{3J;+vhvKOW(UxE`e7#YJL*jrrsNG!RZc>!95B0pfy4# z5vsnM8HIt6y0&uiUs*5rL}#42LJKnvSM_NL5-J?| zn;$ro9D;Mw0tf8>4Rv7jBl70<4RO=Tv*#DJ?pVo zJq?z!11wr2K~)}Qxd^RV^!}(D=Bi?s?vCxaJ6hFLred}i%RpW{Fq}}o#txWUP}P$N z39u#g(rxP9#G_Fe4zFPUgV270^ug_#p(6Pzsi@}qDTC$X%I9}tVxU-KgZ=;KD;T0pCa z+&ixRkxBN<_pFR5c6zxtSLnAt@%wN5JD>Z#pZ&@gw>#v6Zm|Hte<#6~2v&|e#2eSs zDl=8Pwn3w^cF@pHz~g0C%%=++8`2zd_g}M)GN!| z&z^JevMOG%Riqy7_G0ioW=z7~+>$`$xIS#AfF{!wYn~D3GOKd*{buLpo%hREIz0Op z(_Nt@oZzF;(vsr?#q%$@im%4$c`{o)e%T{uq(s91?o$^MY-ZqTUD!(E6uo|Hw{ zoYNrAGR2E&59ZhG(O3X2621wnL?Pr*+iQ$T2RPg_vtXB_VOUaG+P+7GECL82J3@K8 z)vszY_^;pqCd(IMh8 zOuEZ3d12j|PUv~if$mFk5ba+P7$t>%wB8Ti(t-*QBKUzb79H7)nwuhTdh)Q@&MdHW zhhfNk$EI<7=Eis2c7SBm>__x>9A;PlEqXSib&ub2&-}Eclu96|5u*vI7x7zW?PhA< z#PPUyC<)_ZSXScB1Q6m{Ab{npp%{rx64G`y?auzRko-7s@z`TOnkNh)S9i2W^*s|I z^>0A`T=bqKRP$g^wPMdnf9f6u-_#~O^RFOxds?*3cc!!dX(%+A*iUlnO`WWzRP3%7 z5q0HKS*XwZxR;{)wb!b+*aEDIGBkuLC*S7QgwW*K79KwF8!rSs0p%Voww$wg4U7tZ z)@^ElHav0~4t~ZK(eP2v-dJA8;4yx@@vN(kiINX`{>@28wnLr@KZ1UtLb_G+cu z12xg|wLL=Mj|Na}2!N^)AXFnxJ2I1)TGrV32g2jZB)mX;{*si$-U7+T=U0n=o3)d zIo+luL1=#p5bR>#o12qgi8t~PclT?w*+0p8z;&JHo6&)_`qF3Wc z*3R0eQ3FOuV1#hPo$osoRM6hpKD9e`8UR7V^GXvzNHCWhQ03F@q%CCrK=j=*RSqVN zNr=z8CkdMo6TKs0Y?(~G#Z}^lKRfCpg>#$XCxJexI$5T`;>tBF?=kJLCU7Q48V9$6 z_3;BJzm(=IpCxL+r16nyIRoWk;zL!x{E8_#5HFWYlhi`rhQ@ShzPH+TYKxY9!-vs| zTL)O{(4zbLRQLS%E*fsHsDHxeTC@Bf8>=-i7a{%P;eGmA4INNql>I!Py(+eMRfeq9FUy`pg|YYz}2Q7KB0RHXwGb_h&9>ILB^9l zub>dj{eV>XH|>S)Y>QOVtDD(t7==^QTz9Pwacy8zT06}(76SA0g6<9vC~Bq`Ehe?- z=2l!~O`-~SSHMhc`;;A-;BJ>lwNi@ARAZ2fCA}XCwFbu*pIVjXT6cEq0$yz>0u%YQ zu~#ZVc1q6U*CBbifCiZYMU{?*hRP)JAr!!O5VRVi9PB8O>YOEnp$RljtB37dwDPguVMnQL{(BM} z{voG6`6CRt&vL_$wP}p2+1r&8zqWf>utXXU)gwXw2S|Cx)=r5$j9r|K-QE>c1njO^s zadu7o%vtW#%wqQpxZ3PhKx>Pdax`%Ry$SzEu;{sml;aYK^$_ob{USaoc*v`CFk20X z0D*YRK<44@yRD&%n9a#^n?mJrJ^)9L+Ao5YB5G5)@}gM+5)%OGvC)V{T~)|^w+VpR zh!wIGy^zKdkLo;o207aACblh*_qOFueVOOtRre7SuCF|njH_i1^@ddUk*9CFUYTRe zE!0}U%@3B(w{0k%oV2RU%>%;AuvNJ56O)S_K&))lmFv}8{1593=g!!W34_0^i4rgF z=xJ=hrr$|2QmpS58;tcsE#^^iKL(Ofwi+OO_Dd|JsVXdVvgxq$vM`O)ae}fJ zB9A=hFkO`Wi`&}l8a@I?4bBCYwozVFx76bL<$=F<=w&Iml}+0&D6F4ts-s*?PA z6WHpsz&`;SyoaZ)s&EFAm(KA3bK0i`2ZHMCR7?ISsvii+@bW8?hj~VE>KxPr{GBO4 ze_ej0;1H*0HFm1c%;k+;9fEdTAO>}&vh}<2VKQi6#7drRGx?FADJ=T713%*!NpOn9 zVZp^H;bl=6TVMeW!V%zCH=@Bb^&bfghrhYRs0Q2?X`V`phm~@xla5YkeVOeP8f62( zWIwqB?^!*q8wYHxAkeB2t%02-3A=LpJ?HWuC}Rx4kSVOi!!)=Ie#5~}e37IR_K%S0 z^s3?}Pa?7|$eML#Z*F1K?FwQ)=b(QQvH^@XqMd>tNByAXvsEseSw_p;uF+|L?!z$x za!{$l6DE2s21aBtj=tnl7YUSDF zVsD#73V!J&E;AnXo&9@?`_sgp%io zKYID-LT$7tfD1UTe2rX=pfIebZUkUKVtB9oQaVlJCz?Rq=ag}IL^OXZyU*ZeYd6DI z7H4J^oXNsjjNzU1=w-!144>*}0OI}N+Y62nC zh_pXMye3_ad1iFUa{P%(ys{7Xdv|-s_6MyXfl-B+gAg*wywqbH+t@fcfHXzrtTJMw z(Wv?AAe||0#q_WJ34U9VN?*G$1P`x2F_?^s<)+S!>lt@pyD5WApBPl22Ax%tlb%U4 zl+7yvuyZx{ydyA4p*!rwGksAGcRsS8be!*3@GHNBuLC?oIEIBnP>P?^$=uFd+Yz-i zH9d5UWqGh({v--1d}w}wr->`=_*#sx%=nFO6O@kwzXO4)99{vo0$F@)Pw0YqB-I=f zh(9*kkmWEL!iqSJZWCpcIMUau6W(I#C@E+9M=$y*+B*7T3SsX~#+yNNUMrw(kh{*g zPQt^JA|nQ(>JaSPp|!w){repA)8R+uiR2Szdh19O3;O+z>|Z^M64^Pz=H811gC6*R z?dtBkEsFO|Y2PYp%xhtcmm3HB6vo~*-G^CC`c2wi_c9^MU1v%+vfy)`Ti*cr2&%FAsJ0*rl6Q$6G8WH`5YW z`_!=iAmM-Eh7*6+L|J02uUMmq*c4DO&f7(6ucj8Jr;a_gQ&_f0oJNcMWvNUKO~E_Y zUMX!a*Uw%_EpN6+2HXRYFCWkPWCeyhtcKKzs|es%2i8VDiT8$n`ieC1VgrC`4>Lx& zu0Cp1t6D(u9V!VaHyaDt8jThqjb8bUP&5#TJx0G;@(S;vit9BtfNmgQ9lR+gtM*lB zDSC29rB-dBIYYQ2EzQ157YVplst?$qgl|d&wP4|0l+`o{PWdV0UvLOf&H$ACCvbm% z>!RB8D4if%`CXwWZi5s8;z4;8e?n8&Ymo?84vyCR?>qJVqJBLBy}@RGN5|7899M2X zxp@;Y`Cr~@Z-d$R1%EC~(=}N{Nm}XZapKMNAa+mAuFzrV*E+i0WvUWKpx_KyLSy9B zlmU-U|@8EL1|JE*lu)u1R=#RpQmdu5qMrh%f_woJBE~6vXlmVjrJ%e`f&X?;(}{KKS4y7 zU#yF(6S3aBd>SvZ`b9?Fa1#3x63Rn7rq?7& z_G;qSZOrQU2rB&5QgM57%L#Cjn z5TVueG9R$tO(ocI&DnkY#@SxpA04Yaf0iao(O#P} z?>BOOdWlD$xYe;uO#iK!4U@n0FDVLFJ-Lr)nE00qt@cqmvf3k(_SkwC(Ee4U1I==v zj4$!`;p>m&-x|*Nh=p4kp!zCy)qzrMKj={Rfox^}B^0sc1YW}b(gJ*jZwHEcE@p=_ zd7Fuf-il<&T;g@fBnM!o8Vg=FTPwuThKGViD!-lI81 ztr7sop(+Bdk2smCR>gclym-g(Yq;`3=C*7E^WW%NF>&TBC3T3Dd&}mJU!rp(IS#}09wNN%-W~ii!P7{!RmHX-mW%+tm-`qA2qobK7F9HbO zSj?qM?CChHBF?*;XtMz19#UHK_mqu0;I3)`R!=zQQdm@>ueVPmAyLuvJUSu2i!RZ}Av z&`Z@3QCXrl`NxAP%sN^&F_GCv5=+_6Hsy^7yX=wX)-4yhqLTeD!`*6`0agkkZ0ID8 zA#>T-+n|d)rX2GK&aC!9>G4nh=$fZ*^0_ID?uXB^ZN^#(wW@3Q-*a3y>X|yx{a%(q zz2BMS{V<^=Zpdz)qGsPIB@$hZtB(hN+FQWDX@*-q>4z_baQpAFYsMV#uq9Nz5T2a~ zphk1K(KNFnSF`ZJ4mN!gMJ@VT=fhrbOqiJ2H?+XlBQBHP<7vN16?6Jsp0UIDv@H-Z zhcBi}f~YUKg)0iF4yHV&!fa#g?3(Y&G#|{Co)c*mA+egVNNb3<@PiS>(FN(};?%pT z=#y+~t?LDFoeM27)(51D&0D1xI66k2OV_PHeXCgVGbAG8P9TuDbp&RGK3*~=%^C|8 zE3ka8$-fKRBV^W}XTTXCgqS@Z9rK&t1?k2=27Mg7Brt2{?Q%_`mTN_^S1^TyjK< zVeRguCePm0l4&{S^>h2LO14+Ggk{8q>5!q2UmwkPQcf+~4A|1J3`FND(EJ@Cgiw>- zAx;hdZGSbZYqrpdZMHcw3fzWzr`dUbI>kU3G$0gXo$>G!6iYoPx;@zcG%tSEXuKjf zFbdk$9Rjr}%xy8At~Vm?8$TPQUBuLf>67WR4|3Up5%ckhzLqOXXD@$^DPiRoi|V2! ziAu-9vNCd5FYb(I$=Y$1196Y8| zReK%SkG`@jn_cKUyw2N;E0{jx8>~I?5b?UBH?ACELU$3Z_Ov;lzt8-V|6b@YIuq_q zR;&vFMt_4%Hj9WEw@Whnp33=ud4(H!8V zt2LW4WmLu~W%mrmUTOZw(Qy`XzR;EN)pz?#QMpB_wtQTip&xHDjcSQeC4fQes zOysz+QlUj~yeoP|Ht8n@3b@dpBNF~S%-3utzfx1V#)Bl}1lQFzBM$*U2m=aEHzNN; z?bGHwO0kuPf5G=H86D{y9!E-41_GNh=v}9%to162bQoD-(jQLc9ukF^9X_s*KCL~W z*7$>!N~a<$iYYESaoUp4{f>5Q3PNMEYPrZ6UMd;Giwf18wqx6@dn`&*u!(V?GQieJ z`y1JC_=1O@7$djB-T4z4@62{W<&veh62oblvt5M-`ub--`^A`p;Uww1AzT z6tx>EUUrwR|3Ro@N@#uk!JHQ7@J8rf%o4YZ)Y4K-CAqDd4eo4dsZyD!wyG)R{KjCI z3asJ3k#38ws#-TDNnHqzdXJ*Wg=K8$$wEhwx2i6*oMXw)X}uD;U{_n&e! z)+&PYTzaHw*73#1eDU3&a;P7gIn`ueb1BDAbEQa4^>oXaJKK;Qv3UJ-XKC&lPCX1p zeI%@k74!SSyg5X-ij8!pr+^W8JBgOvJSPHu5QZPhW)3&z_mz488uS8$i+QA_V@% z$ck1ASHgdSl*AKGY`M9=gz{mjd8{_KrM}tK^XY1_KIpsUPamN!xngI;FlUVqzf0>_ z(C&pCmS;aEO@ljQC!Hb&xVOXuv#K&UN@?0v_b&^UNLv_PW3Z(WWRw)Lm>pTP>yS%w z0Dvd;Gt3Orj_E!IvHLccEiB+0WU09Ce3rCyl)%FMLqIJv;jCM;6_jbKa!a7t2@lVr z@#n~S9&szzjU(&H;oq@BQ+F2#0}x2N4-^9dU~sIx2I=`(t1I$xY94n%f7oNj$(@}n z_}^F1h~z1z8w=V%0U1~B&egAW!(Vls(R4e+6-0AY7_Xu9+w6Mb=#((4Bz+ybrXd7p zyQM&kM%*<>A%0Vp7qlg$A3G-AfZ6SAuHrTyqglXwFHuLC4M*QdG zxAO@OOKpMuNmisriOV1&3R=ZKJSY^7S7P&46o7KPEZqRk{+OVHrLh@NZ~)1(Su

    *8nI4dW$r|CGoE#EL{hO2ZH(M$6PiBT$)B^?QgFStf z(?ldy5etj5{=c8Cw#y8XQzaw+DMpu`nfLY9lS7J)cK(!VBFg?^_0-WC`|}i;X9}4$aqSpv+|)?KjBm^!zhKF!BN8_+ zh@`I6hbnb`euO6|s3wMq5qxu*?q6I020al^{v zZ?I9!^c8bP@TC66jzb=cY_deMK-e{a<;}y^aG2EDi=p!p&ap@6UNDMrD2;+*zI0z7 z!R#Y>V`_qwOnUNS<2=U--M?m?AGKC$hkBNnN1=Mc_w$X|K(#|P)=R3LK~MJ%iig1# z`1$K`jMTxxD30)tSVy9^Cv{G~m`b7nOK=8N&^^wCW_JTe%^|Tpc&n~QU#h2AQ7oY> zO$!qdX7k;|bQeZCoY^Oe15VOdz@`)&Q~EwJv5>74OSw+(VkuQDPtJ_i!dI<*lEuio z1Y`Ap;vg1bmjNH6tEh77ATQv64E%552ax(O68lWHKmWms@(k4oWP)5#&%^sH5_>vH zFyC|VRymOI*VH0;68*9p1$E)@m_!TrA3upGyfSZi!+tT>PrFvDt>rD=0!jBX zk4);m)$Y(^xlut58}G8pT>W~UM1+w6D&aBgPdA&fOF?P*DA1ix}KA3A^= zE_+rIg5sZJNdpHOqs5B2?iTf~~R&vRdQFxUKf7XHQd?(MrZ4Po?zQ0os zQO?`k4E5lrB~{b?=E_Ro=-p@>|6M{jWDkDTN6 zF8QQjEP9LI&yJ8dDX!ap?$3AwXOuqjMt*M8g*zhCCJy z5b2uW`{J#grY!f=`XwSxEed_F0>Ay2Thlg-L)cbNQ+TnDsdZWKVQ!S;w^lHw;|NQM z{3^syOZ(5&M0{6+kB`O95(!P(fmHh*kCmaiHweO!+GYt|gg7k&ghmiQy}#Jpv%P>-o^mCIZ}FCvPEx`hLsc^z=F(LO0w7 z6GDKZZ?^;c|GUXxzTJ-RY)!>?wnh+FWneB8g{u!z2Amda`(wZ7eRjH_B{9$ZPXl$Y zzxh`f#}^v@1h6U0o_^6!@;NG}JHf7?s6JGJj7UTYG#=~>97@*?QBx4|?N!nWkc~}S zDMNNA8Jj!#d;ak5;~fTogTCwvJmzm|^mM2J6T7eTMBtmq<;uP$ISS&6yx8opYV`F) z`>NF8>{K$#?zUj*TMIfTXSLCZfVCh!ydb?xYJrzd$qvm)ZZSL{C*K!0#PgvyFvu41#Ju^_2 z>s5VwR@9sRPkvG|=b9r;;g|-9U;xygo5%H`ciWCAMwqIHii{<0bT(&U{t>}nh=k-C zzd{A}N^fn+y!13O>SoC3xnvRM&Lv@6wBcXQWhJ;JM}Uq;cbmhD?5FMSdlJp}wEJVl7>d#&6H+ z2U6UHek}u6*NP7W2LdL6EWWis!>@TD)O$G)XeT%xNcs7^Z~evc75K(=zw_2Z0-WMp z@2QC|USJK24B?{VYa-c_wrgkA$01P`ZCas5? z-k`GrMxR5;L8uhN^+fk|+w)R_xL{9R4`_Zscb4is_KojxlIHmhc!S`#t22Gf8bmSO zE3npVfo1MQw=Qmu+v?KjNAqf)JBf82{}DzNM*?B5LY38-EnOV+8mOlO1)XSjaru2~ z`rNhs4p9tg_{ztx5&6f{$5!*Wy9V5mVe8n8OkO%t8TIFQ4?Zaj%MkVym-QBQzjr32 z8xSIqH0Mp8C?qxed6X3=pR9CyG7`LcdbX2ccml#pSxTl4%q0WkizC$mQ*ln&>!p9c z{wl!8owza3zq};5d;7cvRc_*vtks#|lvn4bHpP43X=BHc#pUnFS_?pR4hUwvSF^Q) z^t}K{3ZOWvy~xh^F{&JNNTVpv-`|tE*U+WtmCU8Ax^n}^;oP46=K_EAQ$dD%z6&2^ z3eL~XFK3@-Nm8%+qtwqmlLZ^O!3W#6edn)(15XzZFlZ_?h#>9CI#U_i3mON#74`RM za1{A+EDk}}Iy1^HB47tM2oN151W=m-fI6va0am&NcEPcFF?#k(^@)a;i4p-n8PmTOO@e;= z-dO|1LU6ZL=zqU7t5W%Raa!6juY8|KMY>!448cA&OtJ%T&cOAjOedl#6mu+Y{dE4} zPS>Q{2+sDGrt$7N*%X(ZnK-Vd8 ziGZI_6ztjiCEKA9dxKn0Oe({!m-^SE5=Xbc_)`ldHc~%Xj>MRtUs$iTT!`%9wl8)I zbU)GK-{Pj;iNR;9Hj0k&RCGgF-oL0lIi>aidS2Fj27R`ppz^AMV*I|H5y<|t{>~Ge zG}l8=8LYX?+z}w?9@86*BrIWDL5@1C+MM_c!NKm<-7%vDoW5I@)S4WQH*&AIb56x< z7l!!EXQH2t1`YP+9bpT_LBNIkS4q=L0U8$}#0uUoRz0V^JsNXdBT&~wvY7c?sAx@N zPn>6wm9sVq0kFh;fFgVjkvK-9d)fvhi9}lu#c!uSxlz*B zC^*KX224^*YLS;LUkV-5qR`lW4HY4WQxe7l^r%Oqb11&>YHW1+K5zxgDAC~}Gsb}h zk-8U9#FH>urzshF$K9x`K?OEUP-zROhqShByj<((CC%x8_9k1T%c{tjypEmLOt_%> z(N25D{eyLik9eX<3_7kS2#N4_e)NE@4ez!ZfO1(8D2pfV+&{xlIKOfqw2htxgj0vV zIW@BObYOVe9o0LaX>LopaP~)wFi7SMyp4mR{ncX>25V;@sATw@Nkp=bL6k&s_ zQ(4nbiEc;DR?iUP{xx-$6%Fp8djcZOjIAI(vGQvMeV_|piNgk)$tuBXd#q@J9s#Bh z@w6sz<>*}SzTxI~rI#Q5OGk7EW?!|MErWd~UpU0R!IyXEu*MY$=151dz$ zIxE6Mo`!aO$KzFc|5EXvpQ;oJ=c?X5G6y)Lfa6JRWcLqlmTB#W3`;DD)B$#E^(nIv zhcbgn*kl|<5@&yRxV^_P+wni07aM{*^&rawe(9P&ror4b7EIgc=vfdtpsM^A<0%sA1C|EPR^sa-Cj87Z;dOdIxzF&>$Q zSq&|IJ{%kg;ygGuYb{9}!U0hcB8D^n4n45om#TODNnUNhoFwCd+TcN z7~N=pc}?GgZ`E&=SN6~UVBzYLlSVNY z>6>%F`tM@RZCs1T1~FMuK%V+VP^QgljB`;C#ac<$Y!#AlK-v8P#XO-w?>v zm8c<_L!aCh2_0?x?=ryDMWqW`Q2mK6_sd1-}<$|E1Ef7J05jn>-iO>0vFi1Ejq zydHP-kMyK8luoWTBeLAP#Ex%SRwjcw&FC)M-tCJgxXa_G-DujXAQYIt^kg(=O(6w@ z;CB!CQ*el2D2&Cs8B=zuo`2hWlDdr#2T5pb7WioKeU^g_NElK zas6O$xlrA_*<-EbH@)6OfmlV34((uk1%6jTYWv`JDGww-a&*CGsATC19Ts~RzhZ=k1cio zNTod=;jXqw{q0lPhE1t)&fN~$oe?~EZ*+QnE5@svEL_8b@BCn9U*Pphmq zT)a~luyPJ+b$=L1Pa=fuzAy*?W{(iAxyigFvjwj4gKc$P%>e58x@~?&uY9j>Gb*jm zifR0BvZ6I?z2?GN+jFS07R$|qLRGbClSK0VG)OPFFP6^HHbH2O;YeOFF7r&<5X8*C zUBxuj^O93haRq1FCpGQwCsaZfXmL${yz%uM`&Db0?+|vm4_~!&FG9Bo8aAgv)D6#X z5O5}z$Q@8^i#9{yiAdgAvH~`Nb%E1mXpe?=*2=@h zrik_@IGTNn6uVwB67$gkPFoqimgKyZBkXI#w?u55S8^RE!sIE#KCPt;2F}F zi#dOGa*+!j84Kx$*+Q5+hEh;C*f?yo!b6NMX^7Wrmb@B4dmt-M#&CGLzp}(A%Z5`E zRJbV%<4#V^i+TW5enz8(!fk=I!Hm=AO(84OTYqUVGX8>6blAM~1x@qslECZdu=%cD zW+*B{m}nFLZN!x8<-lbo*LjC6{yoCZJ)H9GJ8se$-1PXuOb58g*=!LvTprmw>MFu4 zR|~6r?F5mSk#7YI4KY_>ZpLcDpP-=VV>55#AB$Brp4y(_X^yz!<9(Cmf3qEZgVWqN zD=wmyRr!JIpLm33fo9;7-fy$RkuKQO*;Piaqp}lbl3v@bNYqI>6O|3o1iD#}2W-O< zv5zLIaEcnB=MMZ^oZSl9iI2rLF)2GEUBC7^Db9+0?zjSdWoC>$zyJqU^2AX%v(W65=AZP|bj)F?-G z3+?`2_wN0Z4Yt^}kP%1sCQPGl>cXA|5A6Ozwn1IZKPtC_6eX8KL&zq!DGB@mqAqC8 zQupw3+Hs`wG_;9zR65!$=z)Ib1W_p-#xMgTAqKIUIkRNCijbq*!{sX0K1Qq(wuJFw z^{&11Jz6R3KF2Ju2uV2eFd<1u4FhZL1Zw20uMUZC=)RdRkRYG=zSve(sM2Ar>3MpW`szN1gu z(i9MqvOZevg?gRsl1noqVOC@b=8NivLDSUWe;?T@(wLDraE}_k=!X%ebLX|)1kUE^ z2^tOb$H@g#V1~RK3@lWN31q*H9)H;{v4V3mS;p?$L}3UdQ9Uv}#aKJU6zZ-P*(T6H zp$d7rsXrkwZTBzo#@scdXk*igV6*`xvnSXJG(R)AJ1R0Y?nKBMjJ}zTH zJXlpfE^%{R!yzfEyGQ!&tUN89zm0tE8K0ijH8KTpeclL*xO!vb#Q4QP+yjbD z@OI`jHGm)5P5|))!IJ|z)u@Oq+m1`@ja~XH!m%N)AZgI&irL%R1uV}^W!;`O2FYlb ze6eIi#K!v!89R^|HD*6un(H_z$7FvCw|fMMfuUz3JzFNypnCfX_AD~`gNRcDmT+09 zr5>A9Vv4Q-l5Fckxag;S%g>JN&aq5a8eU;e6cx z%Kt>{?|dLTy8Uy9k)YsS?=!%q_pR*pXhQb*!)6*)aNe`_JVu^FkXfN+hkj2OhrMYG5I2pALu0c>seCWYwXz1|Txw9oYKE0nUXd{Zo!A zUy&eD!4{X#d+%76;nB4hmuLs-Bi9WQ;i>2oj!XW6YD?0!PyZ(`Iw#Ry^P++X!M~W^ z*LuvT?upzmD^^f>{gW6a*A?dzh98lf@7KSY%D$WNBq8kUFzcHfelV%S^sGPzIjOD& zuf#Bx?h4k^PAV_ySA}>KA4AGI?_hi(@H{O^ z?2o_dnS$d|6vC3~QRW^o=L81>V-mNQf04bm!u2a&Y9hb_nP}_e=?ZoQq7=KP@q!OL z-vm!WY~A#5e)@@bP|gWjzjC`E-4Z0NH@MSmM#??H_*OS0w`aSCBZbB!r&&Y*;N!}7&tBxY()Wm!Os5 zBcrT}TCZ&5Xi;TkVZ}y?37#FWe#*xEnStbr95|2-`*B3*?W!JAs$n6HOq0Z1Jvx^?jNY_FY!EHToe#g?NH)k{c#OIvL#S{5pkep=PI@z2hQKq# z*>Tpq=Bww#sG!%387)Vr_p9OAPmTF^CNrIuNKFZBB)`_J;zce#;j}jNGE0nqJTb@TB6_i>uW^GmFnC<7D zRANwQ9?jDC7b-11UT9J7U#FoC_q*}TPPT`3Nt!#dblDyON&H^}^t%SmOJXOP9O|Dh zlCh#zWa*iKm<9@W26_b8^HyV(%)D)49%7RkO*S6Ea2ft4(u-ooe`ow$qe;JVkvw-Rbd7nvC9H2j)@oh?DV;`7$-My zR3`}{1+inR=QRR5~WSpg-oP{J3bAzB+7`EC>} ztr1yh>Ht#c7UJ{M4#V7Dd~1&6b?X)W5Qp5GV;UlY?$oiZls~6H*m~Om5AxdUx&xG6Yv6y$^Kwo^LfSae-W{Qy|N1Y*R6VeMXSOH$j5`68!EJ1OK1mF@Wf{ ziQC+K(56ATJ~Q@}rH1s1r7<2Yg(`!!?O1MZPz7R~sS>AWve0u79gU~6%@i`}eYVK= z69+;Y$m7o48~XOEV*hN||8{zQ|NR0bN%T}0>fT^TnEuj}Xw8cyi%8N03Nc1W!O1~b z?4txIg2*EZmo#qgW&txp`r!?WmJ;gRc_rKk>8w;Szk;O~yYDj^HFl4&p0#fRiE>=k z%M}WR6Gq;asz+3nmL4s)n9b_>nl0Nf>)x*=In$64ua8!gyD<0FL;*AxThDCFYEYX} zO>+d_sy?22XVgsN8A{q4(`R%&@8%FX`PEw#{r@oaj_s8&O&92nZQHgcww+9DbHa&j zXJXrSCbn(c$;3AH$$Opqxz72}f1p>b>eW@-?T7WOe51P(i zA1(W{Dqghu>rvz&dRV@V5Ofu!VsQil@Qk%Oo7*C*lQ-EiId&H36(q3SfBrH24W>rc zrtc(L1k@PtRK=U{2wNM!7^Zsp71sq0VQgV^8UuCM;eT}JR0RQH9lj3$jC}7r;eR`S z3;UfDzF(*Tcucwkw~0?Bv*HK<1;>kMx7 zlBhpHiP&UI7VwnNeYJfv`4D$!t}=O3<8L~JcuKQHD$8LF{rTIvhkHs1ArCR_ERN=a zk;048s~^aQt_PTMDX0S7>~NY?QZf6KR+}&&9k|yRJyW2@DH>msHWO}GeQCMcr7$0_ z8gXRXdlLwFuJJhEQ_wmT&fhia?23bRb7I;e%=3LRln$Kp2Yvsrb21tamz3}TkK5JY zs%@d_0Q%j$?)~xCX+@b}8K)IEPPPvx@ASo+Rz3+*Jn3*)7XB8aufuh+>h+g~7$HfJ{8K}*(E#>a zQsd=DVLBq^v6dy7k5S<-NLp)SzIwsE)D44;0SD>3tMrT6aM*8UDdmr+i)awNUH>n; zw7mE)nC&vqYHA0T7*Z}>zY7OX6MLNB#!=-HKQsSMyi@CR!CwG0zFWOS|3T{b{e>CX z5`aUuAPWQ{VF9btKoDngL&2^H;jF!o4Szf_(B9*b$5Wmhb5>S){aKnpe{!RD#3#73 zgx07qjie$QogQx|@Kn%u4Ixve81G?gb5U)alSIu=4h!T(i+q=w>_wv}( zZY{54Vo8FBxcdnRNoV(c;K6efyFDC?u;sxs1WI*CV^WOUs z{tZ`0mL$(88;#(Z7FaNsw8{!s#!)xJ5b>02@Jr6Ag&)o9&@Tg=Ne@H*6dN6g3>_dU zC?FV;F7mH~)lF(^Aqp3`FiJN05)RmFt}(Ae6Hbkk6;c3`Ju7hZx*e0(RHwK7wZe_e zEY4nJE6WUdUnx+BJrGiToP$$w6=3$AHHiP0ui?J4M)?1ocz9R%@Du*+AAo4OUhhlQ zcnQc3yrH~hJUkn7%w zMLn^iNX)#AetuW5K(R%#ltGp-3nN5;3POg3$n#b+oahf0GbAhB2g1uZ5D~&yCZ(lvoZYQFIyPw!?x};{9f4F2N{O&$tjQgf*CN+I8YW9k+$STy&OtM; zPdSBqtHpuP+LSk>fuLsvurL_Dl~sqrz}W&~{KSn0(E1ioq>Brr<~qL8lL66BweNu% zEhJUFTt;LKc3rDc=z1zAIl2I}Z%&i^|I@Vd_lD#8e|Oe3`Exyku_2F_q*v9O+@E{c zDuip|T!~4g*0L_hH9LljW25#v#wYEmbqtS)8s;{<0^y_P=)Ccx$E`oeet`NzxH?>1 zslQ{^_JRTUYB9J}G3nn!_hkf`aB;F5R$_4Pt!#7^6c69pShPKII^;iAcFrrv)qnU+ zrD(^|J5nQ2vJX%G9tFeRpO;6EEr&)$&)ohSH-vehLBdl8T&!&h`Vsux<-bvPp+;jA z*7Fk$@0AtH%Z=erTHC9DhyGy?#NkNGUm2?MoCE8lj}<{4eC#@Nw`}}Z#kz4eoNuq% zXjlSJT$9S+46d#4M6tr`goww!H`i+yd+fv(kTF74iUCh=T(jgEc!s_Bc#NrVlkug~ zi}s9A(>cZ}R2X7)k%e|kYD?5+3}tNAS2WWv!_KIiRP?%?@u!V(XUlF@m;2N8TF)5%1HXCNspNH@u= zr;|Y?S#seaJlFu_RfzR58#A??k0tklJ4GrsVOpjY3P@ut60Q4|4(c>&u)Yb1d_?UC zE!}mw#LXw4!pDM^1^Ahl*q?-(yMnYww?vMz=;fF+^}t^T3zxt`xDi&;F`gYC)1tvQ z*`m*}yc+X8kfT4=vgMdPI8n9I7X~=oEOhr+Re-MI4O;g71Fd%5Fd`HcRq+yfj4VlZ4|}3VU=E=%Ao$w| z3i>7kHg#`uTvF`PuT^MSD`RG-Mt8T~iTguVqQXl{B+% zdLB8)k74>lw5Yn?cWB6rCf~{>QqSL{mvZtWVRP3>c!MGBI$(PE_-&Lovd<&scOT0{ z$sDR&EBuxn^E$|xGtf(8 z70P&i*9vgrGtaI3#?qAcqr9wcHFCya6R<8|&G6j;@K%%#VUNigC#NwfJNp>Wn#Oz}4<>jZ<@dMZsmeJb8bXG6h=H;1ru)2z>P zbtmOdj~-81-QP_Zm!1&*0LwD$=uG-Ob%nYt$my+E`qo?oGb&(B2r2=paXrL3TXvex z@5ZuR#^qO?lGTJ%*q7((0EgXK)>W^R)pRz273 zemY8OEYobp%UplQVu}^D-^B*<4fllazrU;G>MB#<9yE$1q#eFUvBR+k9k3^wAG+|a z+Vbucrt|CNv@pLcU*5;WF`W$`NvRfLC!a2$SeWTma0v*3^yKgPieJ*+=dtb6lY=V2 zn(acJVoz9@l@{19XejsCj(ISbT$=1!2iw*VbC^q)!owp!&>h9pvo&&&50KI*bcmea5s@H0HUWOctCaiM)_cphz^$ z5vATK=z6JL`yOlVRe0|}u-Z10O5tDRdo(317V8{vLsS^RezMxtOfgcQ^X6biH*3$^ zcAG6jigddUxKNT2S8M^y%~nhn>Wc;Eia(JrJe=xp8V|^n;V1- zL@1_7+chQ=%89zc)U?u^97_Vg{&RND;0Em9$ld^RtR*{s*XHN$7p#>&>>f4 zJiGow1U@>_%aNJV?G5yxcxj7?L}Y-zpGXTgI>wGwz5l280@6pqOFVXjsSlKrdkDSG z`BW=iR_?sid;(Q$5Cj17dA24Zf^!n`Jo=27)}f|JUh^3iFP@W-i!gq2EBOXH^?T|qBvc>B)~vvh z;a4^K54_ok;W^XTLgmCB0$lB@r3ok>(|Krs-TYK(lJmgUG{6*!ZtB-F;{$9uSn_tI zOVtIw0oG?OV4jU=1pcDJF|@zAtCJ9pCJU25k|Pc4Yr#sTmS$px=fUUh782QFCR-Zg zz9#xUcKL{#kX7FTmF6TKMugB)4-p*wz(Sd(6O+z62F~Y?!ccy>zAgt_zs7jZh&s#} z-5+Hol()@gDxJfO+6IzQUfSqO0Q*J19}o8BD%OmEB32wD;kz_3Ao?2&ivLgbgCQG! z%Q{hG7Qm2YubJl-4aWYlYY7E6r}gE_(U|zd8)srB-Ut}Q){5iIKKry^0KhU4pT2h= zzL%ewZe41QOm}F=ZC8S5P9_S8*un2!2Q&sT)8vIOFM zb!6rGMSU>FrSh{vy%Ve82Xv!V}WDkA@C-apb~#oGdD{|l~@+^Ut`!> zg`IL))~5SA&((U`#{#6EdPW zeai+z6MrWqri>BJ548^tMb1hHgR`It4~Y22h4O#6Kz}PURRA`!Au($_(LaFcXC3t4 zrtS?^ay)v)9G@vBOwYe#&lxH^7t9Kmw&rIfW2mU@CO&(6#E_`SJx^KyfLe~p!Tq)3 z&1af*hFA@lws*_>&!JfsMgNgMaw(VbO}@cbmq4;@A#{g{ zJn~!YWYnhCJ2PaG$CYkV^V4ZRrdSSX`LZ9g6dd#2a7b$QENIe)eTtFRSxAi`zISb< zH|#Ndglw{z9q$ynQZ{_zK>Q4h!zy3mGG#!CaMquEtLbIu#vv*61tmt*bkb3!`4$=Nra*&yU=wNg>asfXCLe)R$h-)mw zI?|u+@1CTa+E;MCyKp>v|3?GS}-QzLVrfUb! z8d!M#TRO3_Mv&SpJ2;27vGVBP@xZ5Z=3`%%UqNMQd!$zuJ@QaHLyVW7 z(-S6u?8+iZ@yV!QwuCq{!$J-rdFYB7I z(iR>b0=XZJ6k3wvq`=~`9>iSbfCQ@Izka{RZm_OI>IO^jJF*Z!wS8_eN)G09f= zL{=CllW4f2!Rb>gXq*}Sc(WRjj7dZs>edT$xx-JFe<$4_rgx8ps!FsrNJO|VrKw1} z+C{ArSK{E2JNb1i`8qJ(lWVAcXBle5@^CJ&3=ic4!+%rxF*-XX>~swyd`68ktK-6E zla`yMf>qH1;bR%Z9ox8w)y4HHxFhN{49<4=>=qK@xg?_FO>D9Jalaep)c*xoJ@R*e zQGaJ+d=H`rKppbKJYNC!m46IWRN6in91{(F|zs{!~Fjp&)A@S8(=JYre@gXi~ zlvX@coy$QNp1QEo{i&KtJQL&TUgRwC-r=GemQVMA=NhrnB&haV?x1;Yjs|i;buT76 zipPakuR?-r;ALmeelNzTiy7snAN^D&lTAwBkynzbp1*KgQDSZDt5*48^s{yhLiC6p z{^BC{2nPZ+1%>?G)6#!F`(L|U*0fZtxd0oi`Vjk=6a%$kke`e}wQg3Na#6zZiEi=p zE#(=Bwv2W6Ci zNS$RDXdzt;s#|M=2x}CXi31F?0HH6G@&5bQ!bJ-RpV^8cy_+a~Y?yCrv%4_5mX4km zuRPx;)FMTZ+3+C;aH#XN%)%xguo=>`-BHOjX%C;w_iuyZ@-Jn#$qnv^4sU>00RHOf z8Aedk1K1Q81*!6QVr&&fD#^`FwRlUEKvBlZY%@(1-t=2Yl_~BEB8plQUEPTh>Iw*f z1pRM=`3uu#GoGOQ_aAApzaw3C%@#jb;tvSu%BCQ&8H#s)m9Su->gssnmfsd5G5a;4 zYLr&_a=l0C_JYQ4t8YbWbz`^>m5yPzlD`OC@X-6b&`m$2TIAdbK-82I$7P~e{Upgy zK7}PXJ$+MG!v~0d+g{TA#}XFoZ&J?eBJ(fX8Q6ZHqLE2ip}>f@1Erc`@A2n zWyA2|j93P-AI0a?>1F;)xnXp(1O2L1kr>)BgmVg^Y6+DDlITR1j-P-ja~J4mYAb8I zFopXYrR8vBGs4vuoM%L!KjH)2|)uswU(xUXVCNHZVqo*r!I@Vs*R*(F1S2NJlT9c+6fK@OZ9HKG19kY;o*eNySZ_R! zAlhTee3ibXX|HjtDVl{EDI|qbuU`tTGThzIOf|I> zRQSMZD+hwr#Z=f@GY`-U+5O>5Oi&Om#1&!EXQTX=|EU^m^^v4aKAeIjdTZe)EbQ%j z57}sWpvK>Qe%cGX2AWVLb>YdFUlAN!#8n5Y_?j6D0Lj%>ax?M6Y9gZNj$aCF?`2R{C0q4Hrz*|EU_ zvPr}_{@!_6*veb)vEI;4@enc6X2iaCWCGjIq9rxTyh~mY)2+VI$biK06ENiX=n#J%?%yFw%<5I_rD(LPZ3hHgI-wXdqE{Xlk;!hACfQf;X1sq`3L zI%xZ0rO!Img0Tb0f+4O^!$l(MTRV*rz1`uWj^PsElYqdK9A`G@;_Ag>ph#H6Th;w> zuvcm7Wmx)2mi{D-3Ne;zb5^FxgHVYt!No5z<+~}z7h0)-J2dEaDd2{I;OW?+9hA3- z9J1|5*k@&aH}juy1dN#p_s?W@ptA$QMqTNGaD4P=;h&coM&nuveR6YImKhs~*FLoPM=&vR^S_NFB9aPSQtMvawUb?TM%_iEq6 zoAYcv8a`rfq+rc+6HngfY~g0^2t+c&_%Iy0eK~Zy?S&{!AxyT0DmCgZBaNK!He#5s zp=p=6lONqpLsaQZaP?=^+{Z+4IL>`*Ab`3w5wcUDP%Nq@D=IC++B=n)d2V2t@F)E$Jqo_O^@yIFK}86Cea zRHbbqG%yqxR#9z-{rKe&aTUAT*mMC29Z=4;&gf|Oi@0nDf260FFwBDC@LN>-!_$X+ zrEK%pHEAy$@%TL|d1xBDkFhEUUkpTN6_2~3EExBgUxyZ{G)VtLHrA(xC*vh0LpE9>>ry*l7 ze+Y`_Sit)!>(pD%Z3Q#*|K@SLu4Lk+M5K)qt{p#|7TxrAekx70%2_ z_VTFnd$Wz5ntTUY!_&< zea)7KEk{qN5)~@gBN?zkSRRv%`7M3Q zLV!?Ww~N`m=fK5d`T$cb&sRuYZudsSQq~+bHi1ip;89K%&gD91%Jx0gc;lB);|^pO z$DNBkIfDqzH8DWZmTU6h=`}DY(z^1HJ@h(0Zu1XidIr_`sxn#T^2Kms~h2~iy$V_0X$D4?p&4={{I$?uVPxeIBnf!F&cn{Vk%q$5&|8frV zEGTb19DLNdNSTb8-7Q;n-@GHFCEy34szGZ1c<|3;CCw}B<6*H>(vO&p$;k`D2fNv; z5js}S*izk@Yvpk<*RXjn;H4${CbD2&!5a!*HN8$eriG1RmTzdmHwsZXw+GOx zJn~8EZPcWRBFRd@D8dSt2I=M!R}c#01aY3Z1lbq=na{>Y=Avlk-jD#FmBGnU(;2iH zm4gf?V30g6XqlX%ytp1`>tkg0S*O#a9cz5CFH6V!-U%@3epl#^}=&I6s| znE3v#XcFq=EvT&wM`Dfp(43}tK)Mu#M{vOiAZJ%gD#LoaUH9E%gtSpH2K!rDoX^1X&sPg)% ztKzfl(43?$mAZxb)CyNz&!3M91Bx>G<*QbwvP--O{4}1WMy16}suzA79FfMzbkG?Y zzs|IxR#I@`-4Yp@I{18c%i(UtM>B z#~$2Bd%m_jZaMaSX*(ZLh|}_@fpCE5$CjwPa_RKa!T}is9;(v&=_SmQ*%`N@HdLou zEva1M*=>Kl-LNaNQ97x5ST9J2W7BWuZ2!?LN&XEBQMEmRAEZGZi76>PxiHm}T8 z8n{^il{*!-=nKY<&Jq}G^ABaNH2M4*7@8aJ{rJkiadL9rn{iC3IkdE19l5X^Hg)a= z7b`gBn)yV=#G}Jm`w8{Gs)@1wYC9S+GlkzPGi_q~lBQI;(d>YHX^Vpqp8TO!`)v-K z#YsQl#>2N7z!sC5tw-;|mT>5tV271&X>n?&OL7_4;tW?s=H?;wGqHQg|G3=Em^LgA zW5FP64oU`Y>5_b2h$LVrkoGDm2Wp0KNWv_d*f+E+V;NSg31#XVzxr?YN%$y)ircH7 z%R~ElAs2WyKnlK%oUY~Gu zCHHEHa8!*>X928fhJG37fg8gEGkL2Bh>5F7D&_TI6ikHKo+s+`Kk~eF|H93vu={;vTPc z@FKATj$W0Zhe@uHW!bk zi;xg~b;@|ZfIxW*&W zM{>ysA)P=dBc;tHo;|X2TY}7V;cTQQ?z5b$=)%FlqPIPPH5XAZ*Yw-(s^u}7>$m`A zKkd1XEw~oHUHXOoMFi7|g>MGtd^QTKyK{xLEWnBx20jCGnmdWIwA-&*wry#d5$jr! z$*8B*$2gWM>MO$;DHUZkA`OSu0P>n&*iXf8Pxf9@JwhiBq`#FZtVxFW-L^vk?_Jpi z*e}!6?MS7+ekNYJA#j(9$$!iJq$bbziakcVN*R`?Xs7QoNW2panEnQr?tkFI0U(j( zzQM(DU-|ZnP`|Z?iBM`Jt_4+;MFLrh&7>Z1eM-~h8L^UIQL0;pO~ZPIda)0E)?)5p-DET40q6V=i`uNT~*^w?5L_sO=_!I z?463lEuxWk6LMU-*986oDpO0G~?)v-Z@8s7Z*2?IGkJqwB zVCv`b{g=AMSAEU37g2$*AfMDh+uM3*<=W4u!lONuwZWw)xwJ8Az{9iBO6j6f38THny$DDq=#y^rE;^xDU*tpJNFe}xgHiytj#0^->`#cLR?g8#fQiOF*x z)c+FFwBjK@TIMz5T_S{A0tb;1sD0H@utCYJhN0Rj_H53_aW+CETq@$*>Y7NV0Rlgj zQ~7)-9>HE8dwf!8SoVYKqSshT3SoF!V~7tH#5Bw9Hrd9U>#Kr&@B@td9$podQfgl4 za3S0z;wIEZt)KqHL`{t~#N?{8sOSQJ`U5Y@QhA-#`DYebN0t zJ5w;sW%Y2TB(UC&dcq?1SeE?%Sh*iGIY2f9y+^RnW#5?)lD9(rET}v+q30A~%{2>{ z`9VCo))*R4DGS5nzDK7oxc%E}F&xAX#{U$K!n-u~*sJ=bzYP01txhFV**lPn1lsxs z;^ecC_K^&yQJscydin?->CrV2oRq`c8*DQ%OrjKgf?FJ#A_JssRq7vA(#e#Z<&4;E zED0aS9}q+^*-G6CZ)gb=MHG_2lsq<9?b{I;Tv|qjE_uj(RvACI8tVCdhR4!>0&)@Y z9O*wT!QTS1V3e!Xo#u8;V5vHfjZl>r=EtL#u{v%?!H!tTgtdpVOt&&3UY?=+FE^TH zsfZd;1RsJX?F~$@L;e2gmO5;vu7{Nyvsl8%m!kK_?yp71IB?|R2^&kydHVyTzssQv z|AQC>0D?>mAlyxnouKyr@;`?J9(+K3h9M(0{vcvbzBvdg@_EmBjNIF&Y^Xs*g^D@dnh7P+ROKpE z;oVsyeN7bWm#S}>y#S^rGAN@ugL=!*G-?S{rE>9);{dB=|2ccecO>E~*OnalCD8O5 zFr7&kwY`Hpsp4=nXI_sAtmY@>^o8fT~(y^MiGHS3<}Q2X(J# zcQ=+*@6^(zHZ>k=d^H+)sRR-P-pk=1F6(5?gev+Xun6qK7XqvT4s5KJQBz8_k`3+p z(A03b0emtH%aKzV)tM<15|#kDg|muNiXCWICm?l6o>h$Z!y0yy?kb|gHK;6^vO+VM z%ytaw6JN2fsk1yI`lDox#ma3txD@Ga-R2Aa^ycy!$X8q>1Lw_&whq2cyp1R9LZvz6 zvjV+X2B=Dki|3F)b!HEFKokvSREGz%YymdVjcsEjn$t`_;4Wz9jbM6Ax2ivwtDOg& zd_$AGfl^D3&bMUptX&L&>4HI+LZPbcjqt-)+_pppH+6tAPa)HIRPw;eP}oU1klBdL zcQ%HPSyLDELYv*;PlCJVnLRvpOh3_2eS~GLpfA2Ai_V^wrb%u}d&TI@bBo~T7wV>+ zvP6(>8?&{nVbD|KfZ)Y~n}HRE90<{x_FEpkk|pAd7s2p?B@7}>Zr;FjO4dfZzAQ~& zoYF;(sp6$gLw!bOG&-TH9s5P`oGJ~BFyN(chPerI_ZMYI4m5}{|M9b`K_d5pd$gO`43Gn0t4h60^^MP z^O6VENkPJGwI6ZW8o-!R)ZqRn7hZc%cboFTC&OaS zm!3sEMI(Qnlp8?SH>nu^Bh?%l01O!&-N8W%?uM{^PNJaXChtDC7b+d;psMrsHyQBl z5BRvc^T58emUX2YtuwZvcp9nLh++8lhZ+N=%*H}J`*p0H_)S=*F8l?&384nE2r%^# zw6#z_{1?pzY()|5v7NAx26w%?e?D3F9^yV__pEm-mBW*7tzr#*pWmnB!1#F(`Yme2 z`jhAVXz~q0ML4Tk$X}-;2t=pLmVXYcXS=xZ0a7tmB!2TJ0vQiI^|d31U=Hra6Q^>X zEZvh>wN2%~H{FFx&04gD8Q3OM7DMwQclC)72xc5F7fcTZJeRI5p&=!n`O74~^A%pf z+1YaBEiX`uQaKM>X%8mHua((`TgF}CU$wupIuS9XodTSX92F7;_~YhbfG7AQWer*N zRzi9MHpt2d9*ioszKD5Sm=&c$9mq1ie(Rn<4^8o@4xgZ7r_4H-EJLUUv=Tpm1Q!DRKd!I^2?x-ScZ6 z((Vr^Q?&<_1RB76Kb^NZQ-SBuR?9K@J0@xFyLFPsoHO(FlbR<-nq3%%k>WywV_$`+ z(hl=Mi_YcmaFNClA09X#tnLnjQmRkC5Kv%$%flkWO|Rm7$adG8-1Z(X#ML;q@`Z-Y zeN#uzm6hS#u5?W~<{Ghd#6I7JWJhiPUf^GSBn+SsDig!f-^CrAU8!KjCKg>i`oQ_8 z#7Xf%-NrtID~NDmU&R9-sa`1L<1n}J$E{OH-%woL#mfE_fXYXSeeb~T$t@K`**4D=Nnl-8wZua{wYNdw=2R3L9vbD30+%{L^j*tQT zI(i^Ye9I|eMkoI)cXoKDJVYNY*)%`J6y(n_BqI``(iXNHLipx zD@WEanfKr5M&2bfM{@fJ&q)GdZR-b9qU+4jzD=64jC>9Cqom9M-AOSLq-sfmyNyHf zueq(CZB0)@RJ{GKNq&(z1?_e~P|AYu4+f}L)_*mFSqw3QV}{q+=<~;HGwx!3|FBJ)VYD5ZXka#Jzvp*a(b z6>&aR5gavXkt>OycVTwx$>ajpwWyy3&+mBZFFsu^BnAr1<@?3sE|`r3)|#ITw&u;e zP9|30?>}(Cca}5PF;YgxsEFN_b&IdX+53~J z?8TdDN71rbH(Y!Q9iB0)UfPBk zPs;-C0XIFY8!9OPe*}BWX+%lznk>uy%BTU7pmw57d~xf=(BIu&tspE(fm<%JRTv1N z@-GL{IibR%lmR`8D*sXr`}^N-D^n;vR{^Qx139c}OwFxN9oBl3>FqMUgsc{hu_cqb zYj#{sKchPnS<*HbYgpQ6hW>AB_yL7i`ifh~{}EWW;ojbT1zTFRSf8?j=s+ z-{L1y_V02%Q5W~5dt!u&QJEhnU(cjZMs`Uuo+-Jfn9?^yQk9f9>0tHiJ|Ayjeer}d%Oi`V&Q1pM0xU42{DgmNfZmq*^%(}f_DW-QYPf%q6+P#H!~xf@ zH6&BmL&JB=BCo=>I{(oEp>Bj(c6vLbdc1#6=g9xdG#I@~bx(|+5>4DDs%m-$d$@Tu zrpC7fb~p_AD0JfJs+i%g*=(8?RGkPdX^@idJ3N;Sm%^yYviC{6^lHD!LovJmDFybw z$Rs~^{%a{>?fLN++4!H+h0E&Wq<4C;B<+^QD%HVgsb!1HD)0F3ygvu$wO*YvllM|Bw6Y5+%A-n*5Yu*OTE6;A5pR zZ-LSnfrodVNgQ%dVA^wuqhFoq?VNgt`**MBWyV_K*CDaa0^b7IhD(s1IX((zBz&^d z<%uB^dPXpDCV%msO)Xd>y^;v-h8+5}9%Pg4$b}D(rKY5fUTEO*>mM=ZTO_~<6zsg_ zROM@H-Rl2gXrMv7j!fA-bTf+n=QkJLi~2VkSpIjB`G46!ctk|ZbO%*QdEqQy=Bq?> zLX1ydv{#f1CW8`BV&6}S=O0==?{{U#wxaahjG*={>QFN2&OgFTN~i^sq!JP zJQLjt#wo)AsWR>V*0c3=Ved_x@+0P3dwQ2~Xe&{2Iq(J#M2{uZO{?ecPd3&3M%$2R z^NNP{=hwi5F{7yciD{X?zU$3SSMHPB>L7b5#AKltJ{V#QJo;U@I&qHs)#<3_#eWH> zf*mc&cZMPULuUb((x77&SN(p9pkEUB1u6{gF)#@t4Nhr?)Kw^FBYyH%TzYdKsJH1I zLX@n0@BUVwy6~A2;&_|Q3H}dxoA#HW&9=hcR^BkDQ(8^Ac}}UN041R%j~}&E@8T4t zjyBl^lBOCJu{s(!q00WkYQt{{t}SPp!$rR?nHd{MltTH;`^o9~2uYVg2c^7;cw%&4tjJa{&~*Nuso)e1Uk(9#hteJcsIk$i*j1=ytB2G5B_ z#l^-;_+<`zW>}+^hch)wXk?a`c~>`$mz8JBDz`~Rux?@8FGP}wy10MxixVLiQ4jKl z#ub3Js(yoUNU}YNI%lz>1+R+VK2Ady6y{9qKF4DphhexNyNAe)k|QXSS!<}Q%Y3dHH&qOv zYx2i58QYgHapp@| zE@PL&2- z$CNEObt`{84yco7vU-2NcxrLmU*$us3OKIe=!NHVcbd##>vrehr_E+X8NqEKAliue7PiPOXN!lqU-C$IHb zJVMGu_;w9fk73Xl-G|KoVLVdxRZ2%ubsligdjgMZPMTLcrrXa1EwyV;nzGL%2{Wkk zCIJ9g|Hm+G0QmpC2`LWyx+r*%elGF@c7ge1wyGQe6@rPirH6TM0SYM}sGly6ioIbr zg2tabpDmwln@l&!cTD|)n+85Wi|!Y`Yj+|)zAv*cs)xk8js;+s-{Q^nDcEzzer#b+ zkioRy+MB?a8qj36@@C;#<6x)2?hV*uP+vQ5-{+SWdn9P@rzZIA^z_mE==SXPdh&F= z1#AR18Jr0M1rNQifWVj9*P*NIJK@*s`Yk&!=s=9YJg}_O?ZN!a56r~SV*HuZKR22p zbyh+1oqNx5QwIfvaQ`ryU86cYi{A-?W`(!6)(Tui*gnSi#_Qe#opn0$au50B=p;}d zI~*<~WHo$>@PK)je-Gw~w**axvsSHWaotk4ciiI%^Qups=(oIns^7^T@!}IB*$!^d zooEis*sP<7J|r~U|2f4QlXgSDmX2T6&sdW-Y%@N{TEwlb10xFCt?U4fK?-=2=^HZu zhKy1lfut@diQ%&NL13c8LwQ|iel^hcD`7ZrWmoVv9oTVXwTP@(%A3e(GQ2RwB(O^c zpI5_30(!mD1!c+oq*f(HfiR=S)Pr*Fs?NnylsKMz8OY>Pb0kMWEUmp0vatGt;|RX^ zgvHQ@J&%Bc$A9jOKf^j*vG&TT>!?^-?$|FGD&A}K_QsrmB_f$q)*Bj=3q-EilF5!y zmhlyaj3G(0(zao&%i zb?Mc%5p|~yb|9*(OdB^G8w@IQt5gvqOVE!_9r^)N>I%1!03Si<)J&bGu$zR5RFYCrqs&* z$iqj{ocTIL$F{8n(j#{2DKf*puYdLuxSZMgqb&Ave~Mr>-TOcC5aJb_Rp4e%ehD$( zI55kKin%k;5px3V20mF z<+Vz#yVnvk&v`8sl@t*>Jnbz+lE zep9tL;t4B2F^|3u$0*`C?hH9*+S7F6Ptp32YTr&FwOs1G?IKZ(<&VbXU6p9Ond!k{ zNCyCGf)5sY5)UD$*wnsO6!GxY2BZc2TVQQkT~W})Nn;kIt18eic|Bi}X~(i3_HB~P z{jPUQx!d)M4fK3dg0!XvAxwSkUPBR{Ll@SA6m1+Ql$pfZhm=g;Juox)zD|)^av4(TZKq?~PC8DpG$q^Vcr*Y-VkMSiN~!E?Z~e{te~A#B`s39VNZF^fv&|Wug*)+b-^b70 zi!w6LJzIpq%lGGhU>{hXt*a6QJ3J_!h}Kn~J0P~Da8Z_+l9s9c1&CpLH0Bf8ao9nw!V0{%@cf;ny0ILCwe6f=162B zFhI@iO?3Bawf66;z985rG|^1M+Cv(kV(%zeIR2*?=#_uN$pdz?rS|C`xfsY4bR`Qc zgFkVEt1rBI$+QpjvwG>~EoW+v^!M}&Ve+RJhWZ1+t2Il@o~E-SX^`3yuT}$pVN#sS zd(ESi38*IolIZ}?=ABA*LO@~85ZDQ#LsSVhN;mDVe_}kv@AJO-&aAF|Fe97|L7~$n z7Aw}6a>O3n-`*xPtGIFxc%0hO0zXb~YC0OqtlZ%itPwM|yNb8~RxM@N;`0?G^2Y@o zx*ExLnGrH+Ke>P-tnV?I}36@hLZ6vZi@`|O}ZYf1}m2!$Wf8^O_Jf%hRmx4x+Y^*>p_ z&)sg}+sKQjNpxIC$<*X3vFWTYPzuVr>q20hlz}~rYBdBT?s|_-?ue39J6`w?s=Jum z(A+8y#g=z6vO|ImHKiiMtpuQ`XotB;a}y=z^U9^s8_Q16IrkFnhJhB*@Mh7^U6{<@ z_LDtit?$wa)x9?cLDWttm+0)%%aMZ1*G$sQkMu}~k$z~E`W`t#A}8T;*i^4Y*NN;Lyn&7L{;`g7s&p#=>ysp`N zL(7I0&sXg`SMh{vT@d}q7!a2{U{UjFK^n-@k6uG8zjQ>oUii5kQn22Rx->`|T};^>Pq__u+j2HAw# z=-IDDo@=h74YZwQ(IJ`N$WH`O$bUk>io(drkEoIRMBl3Yg=||*G}}4vn1Nd{D4?Bw zM;$I{aS#`=V}-B%!200Bd!Je2CS*($PDjpKvj?__pG_G@@6Z$#;M*k$hvPuYU_Tv` zqfTRccBkVD<|nE`b90QYCmb+ya7U9P=_;}~(N-?_G->>f?uP0Fq&A*}^EZ6STGX5GE|D~Z6 zY5wF^q~tKbjPMPkW_;-GX(pkxTlTQx{kY|*(T`8h9KsYNl5*fL_!soMQ>VW4Mw)zIXSc%zsXpN zlR*fbC1TX63ROpfpHN)itwT4I*Vhq<@Il~{si-bOh&)5fl8&je)Pm@eJ-ZCPFRN64 zhjA2osQ(A1sUEb8w$_Fk&DGBH({IZc;(9tMsZ2{c0Q1+Yc$mGRbx@~~1Nh_33NO~2 zlW+NxN@NkaTWgl~3t_OsN>QFhjrFc2Z|8?Wqs*occYiPADy#1Py`MgUL|}|pp0zcz zex4E<&SGa9dwY?VOiLv9(orV+76Ju@B4!M~kl#JS0fe+v>0mP5pitEnLUs-J4B+$i zuC7tq+|Cf(hw_%8Uw7eN3*Q^Te0&YJyZVLO_q(0+R?f9W5caTERwVV##e1h`%}E)W zphmcXPvoz4WE0Td#_n`DZeeb1fb&5~MZ*GB3zx9kyK(oBd!q;z%*`uZbPS&jB=yII zR;<2I6nAS$vP=yLZpE7YymsUaJjwSM6oRu1(>q5IU$$zBWfZh1qM&!W(ilm^Z4xx- z{=bosuy^RInyyG@SpzM{jEku(Fhv7|1~U76T}kA+uCtz8?aJ!KloRDuDl978wR65A z^N9XSCKRaklzgdqZN(_soP&GZScY>b!}MfKRLwp=pM2pOkf?UE@+rOn%Ud0ye2y|9 zA>4R&^p}XEK&a1wxNk`lh#XE-5yPv#-!lg&wI+CVnN*s!PeZv^SE zaL@c7UcjcKXA!3SP?|&zQKMmuKCifRL+Tnd$-E3ZIPQA;fk(dJJ91Xk9O9!f6uHgf zB_TSXrg15nMAoJuN<=)?rlIsLJPX(fKLKAT-qn78WPK_x2421^-L^eJJc~;(nA%bZ z__N{K$y~(bpu2-HncA8V2IR#VOQ8waXZV&tlv5s_NL;`Ek)qE{DrEXOWhex##3H_U zP?-uaO|z1U!Gen7n&d$GV3%WEX*#>}nDKjRD|jI6pT8&RH15}_!)iJp`KkjgDMB_Vi zSd%?1)$~yM&hYjq8zH%WAyI1{rZa<*rD&*QG$67*tyCKr*Vf!~DWadIesp~4$|LUQ zhF!ECqp+cs@^Sn;0zxNe_HQZY6g|UZ)6oD*-$w{W;v^nU&Zq09mK^jGbtJvStxF#f zgCGqj>!J3wM}X+O7dP$f01B3+J{J3Ss3#P+`_F)hMAF4@T8mf07EH#7d56iPT1Dgc zEeeTK<2`Lb)Xw^LA|JWXLoFAQ2rr&A>4$Phv~wlM%zQrM#M%ufr!V4Kjw=f zPqea~jt|r8u1EwrgXL}aZqC1a=aHIm?|7eW$8TdmJYCX7Y7#9^mAfk?cg)OzUfy`2 z#p#A~0a8tbf{=2VS;rb7HN$nKKho{COBGErQ}^Gk zdL4YE>cS>D3upkX3^Mq zo=Kc4$}f)fOAa z*il##GP0`g_u&OphDmfJL!dC!k-EeFYSA!_a-E*_-1tjEB)h=FLxcDxOB!wtpy@EX z``v#oySxxgEeNsbQoSKKS;#5aPD(m(wHRfApC#>HUqOUlA^raj1?9h)>zVmS+d^W# z`N0)$hwD`G{X%^uucscG)?H7lJ4u;6giF~ZaN~k7Trv$}xTug{9_lND+Qo4P<0NsF zb*v;BT^rTC@ntT5N0(XumpcAhSpkX+Qnj@RpNi3$iNfBmv`1+I{nl|`T*us<2U%|J z0fp(1Vn@!3+OFwoLUku=lxH$;!Ob+#pSwPx->t(tIzHpfx^~mZ6c47(Q)mh>1S#-3 zwTuERVM`~~>dv{LJaZ<3L zxz9T2U=SR(n13-oINLq)D7z<8b3ikTPycj}9N9@PNtjKNXt|_kG5d5m`rdsX(gf;5 zo)1!;DkZ|0TA)r+@r`aP>dNQU(ty2diC1tz!ZTc>8#kgn3P46u&MjylHGfWEd8~=C z=tyt{UoSV^e^FeF9wH~#w-9eHLbyAVQJd~~hg7c>`RM63Q!oNIGgyVPj4V=CrY6NV zCHfPOJ2;6c>gU>{>m4aP>3LB$oK(;OPbYRJ4MIUgm630roAdW)3L^X0fKvaz zyuVLW#xmzvD(W!H{dD~#-XNi;B&^}xlC{Yvb! z+uwR25Uwx)4H6AMM>fX^Rx9_0t)xmpfGE6{hduelKsTbBPrQCx$GQ9c*w}nT5Ig_V zgIon7qzZN>O!Du^B*jE_d_q+-*{3c|g8{;JEb$DS_SJJ|AzfwWwYU-8i^hh$Y;P89 z+}||#!8RnZRSpLyVWWH*7Qq--*Y@v{1EJaQ(FJH| zvEZ;x$>-YQxIqNW*a^}a^(^QJ)HG(zR8^x!=yU(jK-*NrQLZR-u;|q7)n=4rz473+ z=)f9PoYsuN+G=xUo9_gJW!+tq=yc`punc$=VLI+n%ZLLO@k?o`C#oNbH1hfe4iFuI zuah#gfbB9F-N$=sXQy2oKut>licSxvM}LLdI`hyQJT_<`pZhEph~QK7d1RINVbmjZ zNnro@yXqE|_kNAV84^vdt}(dt1c2HZNpnanTC3Ah#;f?CfN^E)$;NdC*ME1k5D>pA z!AG86+dtFvp(z+B(^987t?I|+)BO2CWo)|!bw-PMOx46*>By)izaExc&^4GYaJQ6iN z$dKw_atJ$xc1vKOh1xeiZLm=cmbl*glp|C*spuxr31z5X_=hFK$UH6y`=XR+d@sUf zvJNM&%0%pR#jMcRv6-q$N$bY+KyvLx-V(HlQAD5*1Sj8r&8^o%Exb7p0ckqvoqfY<#o(?Q7`lsw4#dPDICGk4uFjnhF zN1&dq7#VX`~ARPgAsFE zY>iQTf38U{>(> zSW%~PBYo%Vb)2VQYdCAzc8W6KB+A$|JoUj_zdceI@Nn3VYLun4R!;4pgi@Qs@vsAl zb*(pl>c@tFjiaEscHp{?@MDNcT-k~pkNO=%G+7UPmIq{vp((sJNb1um(%bonVS)W! z#OVzzH29MXCHd6&aQ`Hh{FY|A(F$ueGho-aw@PXXN^TquX$ys-fEr-v;}`sm4us$~ zGpQ5DY49Lpa6Xk~(%X|j)PyV(V`p;n1pnMQB4?mh1E-D0&(NgG26YB8!=8ZRS=DZY z>9FU_d9ka7M~p`SHsWq9ZiU7ops_3=Ev%E|MlSY+VtTugJ=o@egCpt2+2Aj5meNfA zj8hlm;1*sCC3CKSS|X7%yyN)ZB2OvvXg~JEJaX*j93_vrKJ^$z@`}aDt!I#DDEH0N zeMy1ua>dOCCZ-fbwK73x$wGzZ{yiXCVt~8s!;}OtIlBVg* zFeuV@j2D+%J`*+S-ORV7f8VRY!oI)cJwt^+ivS4?d_;Gg?mi?7k9N+d2L%4|UW>iZ zQLWA~tcgyXp0+qRHD{W#I-a@KyPedBkQosF_lSL>+cW|O5I~uTb+zXg4hiK$$vakr zIdC=7@rH(38s5@*K#gNH3>-dsUu7 zUmIbOh3dZ%+hg{d_el+QXhwqd>oOo)=;c{>vz}Xpe-mIq z<1qQaf&yg+A+GNVd=!_RT+^<}*$b0raA;D3N$&tKabhjUM!q7izllPtAqHMc(k=;$!NIdvfbwqmB-oWXB~ZAa==h z=OCtEaG-YO51-76XfbQ7NNC^bmlS zIB2@VcS1^e_-E6UU$aBEM)dmv8sfN=EHPnR71iakg|Dr>vM8X|YK?{RkTGKq#L|Xv z=6N^yO1T#vNkP)}qp*j3TMgjH?lh0s?Qt#zx@+Cqcl5+uJ%~O$GYpv!kN1#HdCs|6 zRifl6KUu;hTK%U^Xj8e@*^v@ij*n`vplG=C1@i`mQWGU>0uX;`(GF9M+1!7{ZA3tL zzOJZ|qd!*Ee{3-LtC1wE#L_tmAZ!=vk425~{+Ucmzk_(Tv;=1`u@2p$54oq$sJ&pr z56s(SZ(S#1qQUee__Htnf^d5Ps9xtR!U+onXJQrvDYW$2$9}go$~W0#cdggwe+*>a@uBnDqEuGoDaEi9MiC} z`bkzvoMVL*i1v3dJ8?DBc2`n_xY~m$3Y_}D=_pp?AU|`ez?3kY+oU<$NYZhP%lkt7 zRFe>bkW>kIN1HZYko<+#0NK%*fkCg2jF>q33;wZr8&~D$5F_=7n}|ty>HxNs$bl{C zWFY}d_Gp@sjQzqAPi++F(gY}ejc0?~MtH}t_nRsm9%qFswL4w%w}-eCfMNXp0YogF z+9f|iNLbINu(Iplr@h((53gw z3c+^9If9N>amLx-bhp1~j^qvk4qV{#MxTZB-vjq^eSarBF(eTYB|o#&=`N}T z&Hle^!dw3RvcJLFn@pm%U&2@Pg7OhB!!{|;3Qnxmwsk(m6|Gji|5bs<-5aHsyOZ=q zHPfqT=Kqx%85uW*sdl2nvu<*(Y5CAo>YrI{ZE}b+`|s2fajon0<@dKL=~lr1eMlzh zFH+mAi7REt8x96~wSXl=rSuKHtxLoExJhlw-QGmj~g4>e=dW9NzLx09XXKhJIXuR%X3)wH($#c#Jf&4NDoTV*soRQfZey!I)r0S zYR+xjSsyQEyM_EKdIxFDwjnNjH-SmVoH}2RHa*i7X8Q8HV`ll9Ie7KjBRJQ z-UbV49+2x5le2sZcZ)SAN$$h8fS66E4)l=89C)rB7AWxhMCwLI$ z;R!3spSPCb+|nElIdkicr*)91oewPwG{l+AejIG@K6-lbyqwhIlJNAfSOk|2bw3>C zDIJ#!F`44o@3A=HeND@GhW2s#`*A)R^qr|sF5AJ^$o)-$PTKxZU%>c}YxUQEeHcvt zVb81lXJo@{h3Uig`}^|Cs@C`3B%NeyZ~(*Eg*c~K|4 z?Dw$@+uoSpTP5#;L^~*lO+lL9i%b>)KF0hS^P3S>5XUgTuLwh5EngwV7G0P(W&z-~ z;=tsle5#qzbj`MSOYIl>P-gjy>g34o5xt>WLlde~p$=gr9oNWfT_!=L zByYziK_qPEeW~}HYm`Ll!x=zY{sz~1ex$M;EFrTX8U1u)1$nqF{FUcx{plw#s5q=+ zh#N17ct@rMq2gjW=nt+?)E2TNqh+vcH^(TVG4>CPSoyh$U#XZKLA&Wn60ss>B3L8e z8_q>=1u(k#MGbLXwoMy2e{ngb!+fK$_ssN+C^!hXfMHd$acQ!C=pF>&DL6I{5TCwl zM9AHU3Ko-Lf<*S?D7anU-;vsHQo<{5fnBH9&vCuLkuUWkcrz1TX{LVu|FjrYLee|J z58#**<=jpe^ED5ZI;Y^FRI8cg^$PZ11Frhw`lG2HZE)5M#J~yBjIkHJy-hjhhSwTi zqtCozDKAe~pG#2Pu8OyEcmOnSZJv&mC7&s~i*uXX7V9pg)~DlZt3MBPJxy@sdhgk7xj7o(g??x3&uD zk=w_6lWPWv(oTj;(;2D)JJ8`Zfh%e5JI-jHZQfCO5x(+N6FbLW0~n}M^aTQH$HHXK zyL=F6gV9fZa;DC599Q74!$11eQ8{>8u;xhkjAr(%+(llru)DF#!H6<} zejT!#T=$e55E*2Xq`$NMv2L)?ux=Qdkj_!wZ&7jTD2KOdxn&^D?AXhNPD;B90+i8= zz!X?mEP0BY+W1s5Cxms@v<=7Q<1&(?DU{gEu>*udp28W>#|Z@ck;uOv_lZe6S>1{r zroX=jhY`Mw3%1|q`JcV|zwg^wsC0nrn``oy65{Nu#J+@9yP5IYA-Hot{+?c|Q~Td{ z-5<1ip2{%0G~b&ylAaM<^1gTCWwaP&PFVBZDAE zD}~5(7Hm6D=+_vu)Pd8P<9tyY6i@dgvbC_fC@Qde+OcY-jc7kEyrnH1#pdpacKNEx zy=_yeGX~dD{1L{-vvN;#O-t1~ibr0jZjWwmpTFk!FW|W$wTR<4QXO0hnboG-QqgY- z*@e5{cAbMcD9D!U1KWn!ZToUN#xrrSV)Z$0pnloiYHe!SojHEWzAWV%Hwb?8>4^Nf zYs%_5?`YwdVf1r!gz@tp*&8ty4eKkEV_8OlUvWBqJ_~UR3$D9-mMC*z2I9FTFZ{&w z6%Vj{cx19d^`oWonPyYV=orCItAJ2-%@;FBxmY4C#Li}BsBxYG1mk;IN1SNQTX5wh ziUacM&x@(GF{ay<9jmQ@h2*GG?|e};zJ)P!L{g%eKT5bb#j3|8rJzq^^0S|BSEN=G z#8UMs)N{$!Y*c(1PX!lNNbOhvWfZfDffKoxiA4Z{Rf{}+!a%E0t5^*VygYn*%^GW) zAA6G5w8itR9Ou;Hp2zWRtL_=zI23p``V4Rmld?R2_}T*!!msM%Ez6#6O2bIe4ha$K z%DElbnQ>V%Uw`LLeMDtH78kkl#n+Tbo;PAf~+)jl34VOUr ztD!Z*iRNg{Ph#8#^i**8S9a*=19jx2fZE-HTI?G7YfeVU1~i=&O3A{`pyq-jaDE#g zMb?|(#o?s1GDBY$5S&X5xpxi@?M2rjTk0tbvDG#O;RxVYHGKV>qe_$UlE0Tgj8GYX zMPphY-^4a1N&j;@ygb3z1MTt%tWT-Prn(F4A{${ireB&0A}fSgD5RA!yLz680LP!A z%=Ujf0_dkGV_tmD)7Z{g5KQdu#I;jHYQ^&++>$r00P%Cg)Tge|utO%Cr1=J_{~@g0 z#mriC%Ikp0hqzBGTu9(c1qcxWK!)#YX4X@ZF?_8WF`)JYCV-Lfq$e2>tNxBB;{f1_FG7A+a|nA1vJPWb3_&k-xQ?_C{x{7!9X|-#+2m>^YYqtl*&U*dq&_ zVGa{&)Ln(cf<{-03G}chEl4$?>yi-Mu}(53=s8)T{!o!`S>_t7lPbT*qq!19u3hTx zjYr4$ODfPa)-kYwI1bifd+ZEC+A?g9T6MLdY*5T5T*mvcWXL>NP!2g){c;afq)8f) zqgFID$ti%ux3nZf@Dr76{Ta&Yv4d%-9=WJxgsbvKulG2!--Ip zrrG37!qylWx?w}(<6P{M4C*H$)-w%~N{dbk-`8)cJRej}ZHt4Ms7`qeZ0Wh9{k3OM z;I#7r$xTOB0)Xy+t+^9Dz|gM1%%+GR?}g&+3!DxpdS;nyW1siBDli25Y^MMRQc6(X zbGab}?2c}4Pf7ZZ4PtF-77NZBEMkfx%<^1cRoc8)kl7S$$s(UpADS>DN`?;SWXnxL4mpXaQ`{40o~?Duz7b@1-b*Z{@U zX^0~+yR{4vt%S~xllthfHL}x6g9KgpJ4M6Uo-VtK8xh5k+sb2t`QAoCqVONiF7(sMVoP2Iou zWj+%H;tIzeeh&P1uBm4e({hesIy=`;^8pxGzyN}AbhQ(xsOaWzsda`VE6#7=Qv!o4 z>_%b1VAx+UeFUqmaAWP*|GHJu-O+Ia=Bn6NyJr z#d4uzI$Dk3BHtwqg1YXV4N0SdezBAj<_;3s;$f~P<7C0$2gUgyE}QKPFTSi4do_Wr9PirL4hp!u?NnJ*&F8i?P23BHCG6uHz;u^ zIHQ2pxYGG7yH-kk<~l^c+DZny9&Ss6Bte3|X^5LZ?wbEsA8JYo${SkES^Y+paa-vy z?G4k3lLjEDu6IenzmER{MX^sQgr4r zp#x7onG}|xXNeSA1f)n85u^5Wf6i}`Wyb_B(XkIjCtjL47t&U+I{1#Q6<%HLp`GGz zK*vi=b6u95pG1%1!|4^{T5=Pl`D+Y^f1f6c{pZde^aWZfB5w$aLb(V+Ba(@QT7wb2Z@f1md2&k!)d^5>sBZxA|{i}0M%YF+@viZ$P`$xr^!Fw z?Rw3THr)q`H{My^@^^Qx(?SG>nwv)w9KM7F>GEE}LZ{k@To#mkvKyxt!rqp!#xu3z z`xYsWhdzPxQNNbyl5oPEfHY0uOAuSJm>POcT{*`9+FdxhzA44W&@u~5lZqZdEYc#bGVbRfY867%|8(rW2N3|iLp=*%_rwcC6Az4yE zF^NhbF$U%J7e~1Wvr#k^^RwGEaZwjbylNy%?(}b*QnYp%L|I_JkLAg`w>mxqfm_Gg zjUM3(`Y?(ZSk(;K`GygT(m-1(aZ~W&Na?no9nu|&Kr z28F)#t;GkVDS;FT%6ivKb6-Dv*6N;jjy>OSCt#RPspFmVy(%2~IKBfd=1q7@A)piv zM|z&2n9_t0doZ9NZ(4T|rI%z1hsBws1<5@SYabGu8|IOY@48$}E^%bglnZ@5md5RNs0*FMkSSZo@Mm zw*ui1CS%pY6gG+3ADB|s$<*?Z4ncbj4+?yrnv^Hi$p9bSb81m-*PL5UDW-0sjzCEmu%EPP20>zjI3eJ%SQ*&ts>Lzq{EF z6G<@b{(FGBRmv$$QV5QW_DYGG4m=c0xr9%HX*BN+j}d6E+1n2?)k-6{bzux?!u#b~ zP0ptqd*4CPK-S~fk!R3^p7i>C!@uL1N8D!-^+0EVX?sq9(;Ux@HfLz5B^rj^jX`X) z$P`_8=Mw?gGj6k*zGo5P5fi45>Q)EiSAJg1hd=N27Gc`uxczCv+O2@VK^%ie#3x-s zyp@|PJvhHAI}E820b}T82WSPZ{cofMrIX+_(6;-)1wG521hY1ZF+#@*1w=N7Qa{8x@m9Wf6 z>D703Ya`t|Yk2{COcu5VxqCnUuN`r9jJo%_ z3Inc*W114txS{vT%RyX=?Gsh?b3%QX_a{yP*;iDSnI;FRf;0gxzBjP=gVB(6D^gq|yPwRQ;UYlzgJd^w6;e zNZ$lvzK1tKOjzYxY>CmG4$kLll z&0#CtL%j~T>0J)fe&H>)>uhPJqSz{hTfCCl83_$p<~oj-dz?>O#Kxae}}4d4bAR(L03uQxns?rn|Kj8*&XBwe<>=o}1AV@qi>UlH8s+JBDj z(GgTDum&W16&Zop?VI3xB;B^m%k=w0ns5gjp!AAOqWSA=Ld?Hm_WxwO~+Bw8|IqC^e2Oc89E5eUpN)3je!$+YQdMNqJ}5 zh2`;^*J_+7XYC|1aP-+skVYJ{H+YokAk@Ia)d?RId(0n7q`)()n^dfHy~5Sl50v`~d7|lJY~Am3Oy3 z$?+c+%1up4wg~MVKYk6b68RJ_&wwW&H~Bo}>w?K1=X!31 zRK1I(juqiy6gy`|m`THR8-V_!C+`1M1L&VLIa&brC=g{9ppnV=CW_up7E zA>u%Hm>Kw5SIdt{flh(*nMOsJ;1;-(kL8(ku-8inOw<0xMh8u|3+uwIvtr|6S|ZMAy+V7mzZ9vQ7lR{}_o`AWja`|6Uh_&pY@;T4|Oe67h~ zi)M9MsjB`t?PNmks9kI2qKq>fNff#xhj<=4HK3-;Qum&v(BlEudjE3O?Q%#%LS2l9 zX?Rw>=2a<6`-bqT(=^M^Ul84LQ+;AuhSw&x2E5h^*TyK#aAK{auCk;lK%<vCsh#J5~0(R_;E?TU(mb`&m^lo5rv&)sGwoDDbuQtSr zn9hLHuZHtKSc9>HIm5E9QCTU1p&^lMop2;ruCpjo@qay;Dd(fftXpKcU#qBAlGX+O zlez$+2uLMRJ)RFRV|v5|w>^zelv}Pa-)4gT%3qae%uj_0gDTnV#B+N{`N+X?!NK&G zEGfU+Wx*B$9dxB$T-8lcchFZ3gVuM)H;O<2s>@l>EZRTP$+7!1uW_d3k^ZzJLk#77 zpQ=ZiVa~4`CXv-2GCQ$j8AG|eG(wJ|dk{Pp7A)}r-I1L|)GQ~a?vJ+;vKV=^eCel* zq&umE9lvQbA9D?CStT5jQhuobg=f4E8m|;<)ZILMPf#(JkW-8@%cIjM9pVxOwoixU z=~%~oQ$D3tA5`LFg;#lZM9wSf*lFso>#ZW2V-+KJKUJ&lYg_< zAv&x-`)vBkI-dnOQyH-ZN%TvT=c%Cj2fuT4j*Rn}HX-8wkIRL6D|r~=O}HfciO;72 z2!kpEw+cKzhzyyVODjS#HPMLNZq16&XW6*UbGlij6P9^$PQ%a?6u5;5Z@<@VcU1;U zji~24QZel0H#Xl&G;YzUrIwbPyBpoO+NAtbY>j=0aA$iWb1{Lo8rOZQ;^*4=qE^$* z5)Dieb+(7K)PdVoO?`FG&`#GGE0&v4YLSe+`j@^W0 zS$~1%G3<=J&T>~8J{B}xgCJx0%&9(_!&5oG_Np0CrhUELJw=q!(S?#zSnHnk0Cij- z7p^-)MsKYkHb#mjoA^Vqs{@GI+I?o-B*AA^;6S#sm@7*u8%taHd~>e}XNMN1zBv_* zAtKp75Y$C5?F#N1C`QL2KzD3C9v%Wnw_t>Kz(bs&5hc65vzPJMDqFDz5(|z9u5gQw z;8r4i@LJ5sC{=Ddxq;CC95a8rMQ7b26w6WhbAG&xuQIv0YWiGH_7mbr&44cT5(6Nj&8dpirV+z}sW_H~d)!(K2_|KldMSl8e^1e(Ot<&yFV7JMU0FG_8BB+q{-( zfC?d*x&lu$5`6fgTwsP14AhZ`L@r&y=XUfFw0WPhkQt+ltsE6=^?^GWYoG_1m{2Qv zKPY_O*C$sN$gPaVAI~^WoOmBH?4Z`N6r)vzGcD9rZE|*wNq-* zu!t#5@)G8})WPBY13%WRrYh7v*Uz31^g+;X1ADAY7R?_4AdVqAQEaGR)E~64pE8d8G1}@-?*yfpk4nJ9vIjT zK9oqGmZqm7r3-!iax$?@3pA}Wf3+I1*P5>GgrGjf5uj=IFq1||r)jqlqNNHOVa=Q; z{LCRJc;4v`flRSg{!7*>v|n}!{(|((aI=5G010S2)>jOkOgeb`o+-&Ds8MrL$DAD8 z!P*|Wh}4^OclCAmfkIG9DS4s8hs@D|Gq?#7DFfTz5PIPsDAdir4#Zy{_kl|%q!!!= zXC!q!CBn$x=kg1Ysu%E>8*;j5;q)H>(4Q{C_rG&1;*YQ~0XSvftbaT-FTxFo+`*~Q zzC_9zy#iuZ-ioppBZ~5gXQ~0%CIya4tJCm3MOCVw8yz&^A~FHDKaPl~LKP9S03%v5Tj*D=2oK&z}UIY+K-lw{WX5NyK?^LLWL4i`kGMYW?Ur?5+4pjfgl# zeM?Pitc)rx-Iq2xOQvbZsh^bi`-qC0F?6mMI*MQ1v=r1p3?Xlhu|l zQ^UF$m|yAAd+EK(*KLRgbbhJ@)Nt2vVHSYZJ@BfteMsJGYvb5)r#5y<1Jh#j^up|C`68L&lOxKp9tQ)+ku^kO24RRZ1zc14t5aiM9pSd z{2}K5Daby+qP|+9ou%&vF&u6ypw;;_pO;V7jv%8TIcdS zRkfe0y>~sw=lbOi5IU+={}P< zVpvh~6X7Lh3H%%^$v_B}COBj1So1{Cp1eld-d~v4W zAFG-wTrQ}*R6T9MrQBFztlkNlBssNEK63VfFzYbh8~(r3Af@26o(nVY`vEINc(AV+ zVz?TFQmusc4VOd9+c;CKjTGTWW3kMQr2c8&iJt$z@qqB{S&9F>GLrRUHuy}Q^4D{K zE+|+!?w3D0yS#ryH4D@>@jbT(q&&w%wG!d84w#~PS!*iGCHW*2W7JbNjOvu{NiSG< zJo1(QhRzbF0GK$|yw3&jYYtEY@|Z1e(YJRgxVNS#)X91ujW~0raZwWxLXj`)=PR#iy>#bUQ}v21&%$6p!QcjvJT!~9b7C46@n8lG8M8-&%g5$4G>j2$9dOlOdP z+W6t=w3*qs0;M7e`IaGO4fcRX6aYZrKSy+)=lg1i4^(&7icy16nu z_5|j!`@LK;jb*&Xtpn}-GJ*FVMW1c{K(XF&T`j#cA5q_A;MK_)aP>V;gul@*4fy=! zx9RtQjhOp}SIQE$Wh&U^D@l*_25bBreeX662ESH`kBlyM7N z#HiGJU7F}J_$*AyoqurSC>3`Y?-!SS7IR{l7}8`QtEX8N2RsJ zW!)9%<=U8McOs7LR2*%S5c~q)dEDclljM+&i!G*gBOWRqr6%V&RKUe=^jbH?RPLY@ zOvh&)E5Wr}Z{VHfcIrt~k2ChCa)~V|U!Kw|#fNAL0F~q54O1!#Se(BegE#Dt?&JF9 z51|1AG@JvCWZ(M}UVUqj@Q0ml6B?WGSrvAnXg>JFArh1&;H2*>Qj5{Ke3XM#;PDWP z%cujk1idX*$S?`rTLa>>3n~o)#x*4IXJLLIKaAoZdpSTac zOF7v4G$TXtH@dQY;D}j$k`AFtC}-$6HlI{9#Uy@s>9ccFVHhr}TU#qjE3{Epx~d`2 z{unB+Mn|oHW{&|z`K#gT7x}g->27=HPwZ{( z>A%*iN1Xe6SkYQPra8MTdcb)LSADqV|)Y_Ry`<;kAKvYBaRLeeUz!U!1j0x6!sWh=Iukff`rgOK(rj6@AC&4jB&F(Z*O%i5RPJw@G>UkgHNQ_CkENs6+E|=TN#bYIHoq z*V0uw4&F090>V4M|Kd_v$^zr+d#x)=87i2EsZo4cHMSC2FjVb&&K{dA*JyD_4`@v?q!V*pzik@1DC>EbSE$P+2eFP>R)SGI!puKc)J zNL_x+WyI+zBh7!tkPlK;(Or?H)4ey7>1h65mD%H%H=1_XP=;mEv@mryBOz`A$WhRl z#BH7d)}Nsr-&GXxy%AS9(B+-O$%$c%2sN=2l}FxUD+TgN6@!PT82=5PQ*O;SI@c5d=h2)>sueeJkEbm z$#Bx=5Vi5iofFJMXTPEF`Qm+f7q zXt$2od|uK7<{zB+=FuCqAj1=_B^5MtFCz>mto%bA_*#m$UL1sPfsmr<*gUD7Vf^F= zvO0M&ebUQa;l>;Tx5gr?)^!XP$1!(IUgZ&+9<;*g%Ce>a3;)6Jc*`0_XRBJ;ZE z=~^SEl4!bdjmS)^?J?=c;MFp-&egBGpK8=z3e<7R%=ougT1h+nmnUXC=aTP~m9v|U z6O?;=1gMdoS80>5v-N^k{lWA^YQ4Y6O>AMS#V^ki#LVK6PVP?Fr!~F3p-^pxB`VkG z;0*#ADxX7YR(`#mq}}FkWy*P8UZ=IzT03zP?%P+Ub%qu0r$_%O2u(kzN$hN47`$9V z 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%0DOVYRS>>Z?g#~ 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 Ra32X&T{{a=sU{C-6 literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.crc b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.crc new file mode 100644 index 00000000..72f1ff64 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.crc @@ -0,0 +1,250 @@ +23ad177c +1b9c8cae +233b5afd +e23f451d +b3e6e470 +e204fec1 +36cf5e86 +fba3f823 +7b86762f +7de7535c +0e7a5291 +a8897d66 +3e0c1e91 +d7d29155 +d7316205 +810a2ae9 +92dac65f +d323a7b8 +bca35249 +1e9ad2a6 +b6fd4f82 +6fe8fe2e +bf949b74 +3f59a84b +85e1a39b +63ac1653 +ef92b037 +a775daa2 +5dc0dd88 +468bd51f +cbb91ea3 +90b60e7b +bf1ae070 +1c67ce82 +7ad8c84d +6ef5edd2 +05f75839 +5ebd62ac +d35c657f +a1dac6fd +0b2f6044 +734d13bb +5679bd57 +fa199a3e +a4a4d6d4 +6bd56dbe +d799a86f +227bbf58 +1541656d +c723e6b8 +e07cc71a +04778c4f +fdd8b676 +8c681031 +e6847162 +8c7c211d +5035e333 +4c5c8b3a +77401793 +8d15d34c +3587624d +47307e2b +4480cb44 +fbe55c59 +6520892e +39df34bc +8f8754e0 +3b08b12f +bff1799a +0034759b +68861fbb +801befe6 +c8101440 +cc5b856d +96c1b94c +a87e1845 +18b19080 +63e158c3 +96e5974c +3faf3f86 +5947f01f +ee3c1a46 +69f11a00 +2dff59fe +d427bce6 +6bcee14f +04a10cc5 +e3e12f43 +873d32ac +f986a99f +ac98dfc4 +c08f5c03 +7649d9f5 +68b76170 +7f8c6613 +433f4970 +9ec1ba95 +f7b03143 +37873456 +e2da340c +c467fd97 +bf68a9c5 +dcece5cb +5aa3c7d7 +9027434c +5e4690f9 +79b59499 +3e6c0f04 +42853006 +9b761bc1 +2f2d290b +12595217 +70482029 +770bf015 +f70adb10 +4189e078 +a4619a87 +5b2044f2 +2d162e58 +e071f345 +f845ff1f +c41a2258 +c300cd49 +b5209b89 +51490639 +d5356f60 +a232fd4b +0d98e5bf +d34b3e3b +9b951312 +b2cd5ead +d9bfb278 +6018e793 +e8b2f310 +a8910a6c +accee1d8 +6e51c606 +9050757f +aa752e7b +a4274821 +a0561066 +9258c326 +f6b66d22 +0e2f7048 +84d7bd75 +9a2b13ff +7db691f1 +e0fd2689 +8fb24bc0 +89b201e9 +041dae30 +c8856390 +468220e4 +1371ec57 +9ddb3769 +76aa8d4c +2e178d45 +7519b38e +23da843f +da24380f +34e3b146 +42735634 +206269cc +22ff4630 +8599f9d0 +5353b7b9 +f198b705 +27a62538 +2b6309a1 +bbed8b85 +c8c605ba +23cc23aa +a4b7cfab +e8adab6b +50308549 +82428aed +90e1a158 +f6177648 +63f1a6bf +53dd22fc +e47289f0 +f0c36a34 +fcad085d +fd925241 +32a79fc0 +047d4d80 +d31235f8 +5c46386a +aaa9ee3c +935a352c +88df87ae +e4dd9b22 +605f46fb +087558c3 +25461a0f +fe564aec +1dc99d84 +7c2b1a2b +08c433ce +951609cb +e69a15ee +0be31b4a +b0988d03 +c5c0769c +7807a352 +925e5b37 +7dc3aaf0 +d1621353 +f1c0e356 +49a5f2d8 +2a7f52ef +f280d5b6 +4f546dc9 +af08b467 +9612c8b7 +360aebde +2311a9fb +c175e073 +966537f0 +1cd52932 +cb098673 +11861fe5 +b482ec61 +a3d89823 +96fe22e6 +d28c91b1 +13ff2ba3 +998e52b7 +ae6ebd46 +20381724 +a0c7113d +de28b72d +a50f4d0e +f6ac0144 +71679cf2 +f354c9d1 +6d84c275 +ca0b4570 +95138a3a +e943fac5 +af611c9c +d5811d81 +24fd3da7 +90ec3b70 +300659b6 +1aa57f0f +3192b32f +34091c37 +b7bc0a9f +ea1f6fa7 diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.json b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.json new file mode 100644 index 00000000..521e12e8 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.json @@ -0,0 +1,260 @@ +{ + "profile": "VP8PROFILE_ANY", + "width": 320, + "height": 240, + "frame_rate": 25, + "num_frames": 250, + "num_fragments": 250, + "md5_checksums": [ + "bc240e9132a8e5811ad99c325dcde3bc", + "c372d26696f01dcc143408882faadb70", + "5f5563753c2df9b629d8f840948ba868", + "4946a5ada74ea61ed8c1b1fb21014319", + "7ca42e2f308123057992591ef791948a", + "16b4106a6a9d59025e5bbc66f07039da", + "3be5e5c68ee66e2fb6ac064324c5c87c", + "ad1109a6b2cf8686930420171c9670db", + "2f6cb449852cad8f97c9da5ab78a9938", + "710c2fb1db7b81cab65dd5a909e5803f", + "89fa074520e5460ae2dd5701737d7852", + "5cc153a8ab898ab88a63b262554e3820", + "9767b593e5c91694a79c37647dbc88d4", + "f02bd899c9258a6b2ec63bebda0c84b3", + "5a360a4dd79112f218ba9d7a8b957449", + "8219c7f96304b80376bb9f6fce0bc1d4", + "bd5042aebf679649cae047b8a6bbe668", + "95ae971f046ee08f672ae6b62f140324", + "1cc9ef2bf9cb505fc73082caadf39830", + "70da140b71aba0331d12180279f96ee7", + "6034e11d3e6c701a36175614e0fa3d04", + "81208fd7ff8b2fbc137006c8939bf506", + "9c6888d54f86112c9b566a3194f41153", + "328895efddda13025602afe5a8b20738", + "3eebdeb880bc6c7dd4e507a80e9fce8f", + "a61b92de42dae71fff84e88311c4101b", + "f9f05c92b7313bc1c3c604867cd136c9", + "6f7d0a3be2a45995bf9a6908009dbc4a", + "228d61816bc7f73349e540618799b135", + "bd93f9f651652802104ca60fd66080d1", + "b60934b38f2b5d548daa35d124a2ef1d", + "a4ece70b74ec3e56c6a5dfc60c1bdc1b", + "c4abfd482fad6c516831a4a58d2d0774", + "3295d0bdd9616f4d05460fe8b2e1bf40", + "470734cfc97e1cbc3bc4207035a34582", + "6e047a159557fb1f07a15de30f5df917", + "5701ef62e4c8a6dbed3a6c52b0b848a1", + "66c0080bdecd206eb91d6e81b3c55a2d", + "904013705de2890b2366ede1c2bf642b", + "8cdb84d894f9c9b0987631e0dcf66c30", + "25aa3848709c9644c1401fc659457633", + "b5dac5591bc9b7ac6402d6a36114efbf", + "5c92b763454938f74a40c5f6e1d8903e", + "7ef756f82cb7863747142794ed363fd0", + "90126c97641509200b45a85a2dd77f47", + "7761e60889c650d5917a3fd824f07d06", + "df8386744a809827699ad7a3a031b713", + "dcbb1313510f1449d5263afee3d8b4df", + "f311fc18af5f95e2f97c6208987892a4", + "23681f64f8f05d0df4edf0634f5e4f6d", + "a30b2b0283f1910b3436ec46fb81af1d", + "e5f96309f895a50e25f1cbc272bbfa73", + "c6a5160480c74fd96c2d2be793ad327f", + "81414ee1a242bca4e1bc13597d67ecc0", + "b4ca922ab8a6cd5b79e24fc86d835ada", + "15111b6227e7ed2b566de5fd54dc7004", + "288dc866be7addc25f6f895c06049832", + "63880dd1fa2f86cfee59c3cf488c782f", + "542837c8ac7d760c0e30dd2e0a7cbaa3", + "fd4662f1a958b84538117aae3b6e1897", + "1582acae1b40aee2024fe4426cd643b5", + "0708cd6fc5dfd4248c8f5003baac286f", + "867c23e7b433c487f4540607f76fd0b3", + "dbf6aff6fada60ea9f24d6f71cc3a671", + "1baf78c6d81d3befb98a067a402ad71a", + "f42b8ff572ff856446742b6161a5ef40", + "e2149f80975ea267e029c47e9f73414b", + "83cacebf4c67f829124a010baec713c1", + "f1489658f17367dcec605358a7d67d06", + "6b0aefb2ea88e4c0582b284c13f7813d", + "8fc347fd10798cbafc29c12c9ed3997d", + "2795cf340a755d8cf6a3315b5d3a43b3", + "f70f59e42030afc271a40eb412d0c04d", + "f55a2590b4a8207e3953b82d3a98bff4", + "d10b4d289bf08969c298a4ec632a9000", + "f4438d90b3200966cfadbbc0f2a60dca", + "38323b8e1860684f56a9723f2e25b338", + "98c69367af34c195f2a8fca0a75257e7", + "767c17518326adccdbe13f06fabb3757", + "502f9bcfc14a1693a1b49b3acd3ad35e", + "91cae0dbadad0068683950dfa0d79827", + "ff3e589e6d8961158c0d56de02a05503", + "4f2bc1561cd057ac737f225625cae612", + "ce1701c2ddddc3eeca0b50860a5e6d6f", + "af0994a0d32f6a8d5a984b3e411741b2", + "c466d7f508763e52c9cbfe4fb82bce0e", + "7a317fd7084ac60ebd0fc010e02a9f14", + "5cef607ca64566c82c7338645638b345", + "2e2d8a5e8e7b507bd5b45ed195acaae1", + "a16c7396758df8e36d0b8066905e97ec", + "1b5d6e56ac37d69768101357b0a14958", + "a210d31b53da5b0178e49cba5a579867", + "4cbe59806ea2fdf4de2f18401db01178", + "391242b0780ac08a52ed7f39ea2e298c", + "25705ca6158e95a47d4120ad77ac2c80", + "218a7ed5d30ac42a9ad7901c96628a97", + "f11f5ff53c402947ea692d20a91c5f9f", + "aa916be73738558e653312d9a46eb08d", + "3bbc39dd7872f360e76d4dbdbc0bbfb1", + "9528cbf3d0714b2cffe03f9b97578fd0", + "aec906ea96a00c15e29b006e5b6a6cb8", + "213fc28804911a5401f32f5c8d10f768", + "6077ab996d989d5c7e093cd4598aa27a", + "4f87dd51d5775d6581aa5b3c64218920", + "f3d675731cf3459c40feab10079832db", + "1d3f49124e94cb40c7d19d8d1be8151f", + "7355e0f736c00e36f24c90baa9128440", + "8302f8749b758e2d7598a7883575a71c", + "475013d5dd20208aeebee2d8ddfd0829", + "cf56e915ae4a9f9e42e79ff2443c8451", + "3fb3d0ebb7d6af45828662158a72c455", + "5079bc317882c229a860b7f7437d3f49", + "1af2ac88729ad3833a6ad78f6d9e2cc2", + "1c1db3816ab23cba6fa389d3ffa9cf1e", + "51d6badeac26c18b82993bf1cfa0c0dc", + "784bb7c12cf105466341199502e1d80d", + "b994395d3dda49bf8275aaf2f1f6865a", + "18d9b4e8c49d4eeb3cf5dae7dbe6b1fb", + "42a82e19bb3f84af20d9471cdd39a0d2", + "398c5f742a2e7fd3fcb9be1739fdcead", + "7ee56e4741187b0c4b72b1c985445c48", + "084508f970e24bf9027d537e969090dd", + "8572ee3182f5000d52952071c92d8b7a", + "2a0b5c8f2b777dd9651c736b058e06ad", + "e1439f0418533767ec995774be1329c9", + "74190710edbcbddfeb16b10c3ba35e28", + "1ebfc537896512ab5b7f86021d3cf6af", + "8816b4ee72c53268f713073442ac102e", + "48afe1d79456e91852178a1f491f8a69", + "94af0fa8293212dfbcf955a2dc0ac98d", + "d91a67bfba13a2c12df79c262e1445b6", + "925a297e8ae387b4054fcf8ce102b03e", + "8d1bba6516db3ac0c5819fcd1f9b926d", + "9c953c8883aafe9414014639c8e899a6", + "0ea0e84474367cfefe2424b6ba059f65", + "fe95e36103305c5983f5c6a5f76723b3", + "a034bff592c7ecdec779bac113b917ac", + "47b68bf54101994a93e2c62cb97e3cbd", + "65483042d4a9e95b3fa3de45451be436", + "d493e0314ab12323afc190e2a3f01caa", + "e34b3f60343e1b54206f96711df186bb", + "d6a6122214a9517c1508e5891143b9ab", + "3f4c72eb630371ee5fe431c01ae4f909", + "62e7731944338b0ea7de893fb97f2178", + "18e712335b9170167bb48f11abf42509", + "ec1a946b3ae5158fe1aaf2e8e0efd712", + "504337188f1520f40355d81c7480ec0a", + "f1a385eb4b4ab170d02ac6432922e4e6", + "222f0577fde0aaf6b1426e1a9b8a8b54", + "b743a5f9de687a1e3d6b299fa0a564b9", + "768498c5b4750a09eff73070fce33475", + "d4282a27cf0287bdd40affb9ab859ef7", + "21266fe0de9782d46e66f0707123c168", + "f61f45a9350f364225b0cf3855707db7", + "e77ee78872321b5ec8faa83ed7b29eb3", + "977a598d831f37771e9688c88bcfff5a", + "6defde14099ef2d7097c0dea8da0ecec", + "0e70a980ccdcf6ee6d14b15baa6e2b5d", + "2a3b2a2c2171f69290b2920118d322c2", + "19662f972ff22b879e693f4086166804", + "aa2b08accaeb61754494be212c98805e", + "21065c59d5a769987a839f619ab576eb", + "bc075a46e495a01c323a6fb523cdf0db", + "36bb2b3d70409f346a4d5eb093ae1886", + "f8605dbab65dfb05b115644776e83ff9", + "a006c8bb5906fee3c2eefd446d39fe95", + "07ff10669ba5acf1826ee40de8c67c0e", + "12cf0926c15b821e053f29e7b1b28f45", + "95f091342965e44fb8eedd3f9457d22c", + "508de89a43e6b2d959f26b787e14bef5", + "5f2e843e1d8f85df4b51814efa3abb27", + "6dc84686dfec2f7fbf2e40d159e4260f", + "99827fca0f581e3d317f60d09f001f96", + "5029eac9e58cd1678d093f4ca772953a", + "eeace9fc43d56eb7e17a42148023f8f1", + "ffa59371f828a06dab6ee4a963912654", + "6ac77b6b06185b95f59444cf3ad6bc78", + "03efa3bb002d35cdba02e00da922f696", + "f5bfdb2c5c24062496133e890cfa2754", + "1c0c256dfd0bba350085caf70f381fb3", + "53a1c9dd7c60c475d5b82ce05807c7d2", + "88082f421ecb6f4de3b0bba1e804e4f8", + "6cb20eb92b40db9d4de47ace5751ec82", + "cd03b22bad2fc0a2b4d25c330c2c1550", + "23d07159e4186ddb5cf9b83091655fa7", + "6d2b49c346058e88630bd2e7130cbb6e", + "f6f7373197cc8b8e3578a1dea5ad2de5", + "1df3fcd9056037172b1f7c2b5d968adf", + "0e9fd2da8c2fbf66ceabd5f6542c7aea", + "c66311a45ec65078430aebc0ae98fc47", + "1bf70e685d62c6895595821ad4fe750b", + "f4a8edbecc496180e426b727e95b5519", + "716c2bbed41dfcd10adfc8b8d69eb1a3", + "3d895894d549dc71944b0b82c326f51d", + "fda65188faccf268de738bd4b5ccf568", + "111e4dd717ac60e583cba2b55e0c9dde", + "f5baa07a0a59ddb7889761a08817ca90", + "a0aa61efed813f649d8f0c4a168d19ce", + "d8ed7da50345f1c28b0541aea90c25a1", + "3f29b0c0852f2bc1f66b419f210a9c5b", + "c6e54856a1446a9da4a15158703c2cae", + "1ace1f57ed2673566172dfc4fd44c62f", + "a65e95e1b552bfa949630f4a334be3c1", + "ea815544c8f740ef938ccc6bf4464279", + "c781f67a52268fcb623b6e6bb802d51c", + "993b8f6f6a9d47c3f5bf17d2d3d87334", + "6ee705b08f521beadb85da78690c6a58", + "7dcf1db2944fedd97d6a328f819df8cf", + "7604f0a2c9a303cffd995c55a457f674", + "724bba7f44dabede3d5de4d354ee5e43", + "dc2bf3455c86d85f0691c7d0833dbeda", + "4fe50dd314a475a4e2902bdf541e588e", + "620bfe20f70e4575ad66f3cf7d31c84b", + "97f38fc687b9961830eec12cf9941f49", + "84fa8ba0b8af35866580f8abed0e3999", + "0596495ed4793ac4435d7b25525293d3", + "301e430e3984ce628d35b21978a72dfa", + "56640dfdb05b24fc291c97ca47ff4cdc", + "5017fd7beea9713d5bdd458f1c595274", + "aaab29950ee235c0394cb886c79eecfc", + "de6bc440fa4afc37ff9aab3147c71bbb", + "89991ec60361df96b6172a6c1f083f8a", + "b24db33c3ecb0920f31fdb2adba5f116", + "013e7ef451938877d37a1803e9cea45b", + "76eddfd809cb2502631710856f136141", + "cabc8d3e508d2fa7b9a436e26a1ac6a6", + "952ae0179243ef5f47e28ae24bef38a1", + "b07a01ebb562aec73f40fac9d4d6faa2", + "dc54efb2d6fe190f9010b0f57d47f674", + "c428c73758ab2f5f9d8eefed8d336918", + "cd05dc0a3b7fddd5ef7795017bce81f7", + "a43f69282bf256d8469aff337140b7e5", + "6d037e63f3d0d911b56786f551a79f20", + "1c7e0ea5b46dd102b10852723a137243", + "3da834fa2dd340289063c16adf9693b8", + "eb98bf21f494ae954eedc98c09322294", + "99f8bdf081135f98ad28470f87b32a6c", + "6ced1f88c4d3533917a3ea49d1d11e70", + "4a7c635acc8e9313c141c38593a6d404", + "2b551b2a4dba3f92b35f53efc0c4535f", + "5dccec58b05c2ca25c00b0e41c8f7291", + "ce313f7dce677bc722b7cd289daac665", + "1b1fcaea5592852d3c35b9673e7f312a", + "38af688e1ac1c5d558f08c87fbfaa18e", + "cb17506ce7a45c41a39c7bf3daaecae9", + "d7195449baae1a040e081d2a55ec7a75", + "1870f5ddcac6c4ed14f746b8d11ef361", + "7c998a26a20aaa3e348f9f7c49407317", + "3f8679de7b0d2d32ec3f5997090e86f5", + "17e3fb19dd3b57130a73e40dca0b136a" + ] +} diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.md5 b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.md5 new file mode 100644 index 00000000..5be6010a --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp8/test_data/test-25fps.vp8.md5 @@ -0,0 +1,250 @@ +15629f301f2aafa650efb8eadde9e253 +629a2144f8a05941044a98be60a65d12 +b62c4bbaa3252dfd0624d006d361589a +4c288f1d8fb3ed548bf8136533bce914 +8ccdb942cf3289cfdfbf5ff1479f97a3 +a9503ef0edb03b01fa37066f350cd618 +2e01d5c69105bb71113265c8016d4281 +7d83efbef6287f12dbe3dd209704f0eb +e8b9c5e92096ae9d976f8279b6bbfbfa +7181c72c75007ea75e9dc7f092834c01 +c03815b113b48606978bfb3f0b9b39f3 +7e0e73f960f724262821839a9066b54f +2cc580feeab6b62a2dde8a59e91234d6 +6d33810469b7170adbcf465b0f45d7f2 +14db48679744a6697d8a17eb7af9e9fd +6b52b7170df177d5b175b8452a748d09 +9ff235afbd500dc40bcfe03a8f825525 +5c57f7397c7d0b681a3245c3a4fa15ac +581d0c5bda5845e958dc2fe09ed3e8d7 +b6b1aa3329d564347129e778d91c9837 +1350d080e510696bf3b378014127647a +f9fbe4f5ff44ea98b3a54fc31f5d8919 +b6b2bddb2e0ab580d672f7637f7641d5 +056cfb6c29e54d8a873e3b766f324b3d +749d25987f8327cffc995581ab5a9b34 +c71c4821682e0a96e93b746c2010f1ae +3ecebb74c80d51b4f17409b6d0e5fbc0 +423b4eef44c5f49408a10d3162c3a802 +308225ce56389c53424fec143273e07c +adb06f5e0c01414dde6fe1aa95c15c60 +85999b4d9654c68ce739c343c13282ba +e6dc79377b1e3bf19ea2a1495acdbafe +ee509e685eae12c70eb30c855fcb4053 +df4f7bdcab815f5d77db5e3989c41703 +49fb116cc0129bc9ed397a6cf67cc5a4 +a15278338ecf080d1fc4e2c58f2774a6 +4996bf880773a84c0ecef7d1c82ad541 +9be8f61b0bb8815bb1c6c8a0efd4ce93 +6acb8cab539751704edfb312ffc299a4 +d7212e365eec53964e07b219aab9be56 +5e5fa1bf62145c5dad62110e77754869 +aac5453d9bbc2aea583d739e39305d34 +32447433753f7a52e294418cd7cb9f5e +eab2a9ba2028ed4c332d63b3b74f00aa +2ee6ae3a590aec2433b6e106f20f6efb +87a0b6febb5a750c09f8ef1d7bbe3df5 +db0ea40175b7f1c778b0974c43823960 +6100ce6240b1e15dbc44e2af5863e7d8 +a33a77e06e2686f9b69d717fbf7ae3c9 +0915cbf3d4ad4ac41ec5f0e59ae15827 +b334026cf2df3a7b1e7f574898658ce2 +fda18019fa6ee84ece9763fddebc1924 +6a6346e0cb757935bb5a92d7eeefeafd +242ceae42d60607eec026222937bfdb5 +2ac6ef0ef9598705412d00c7d2efe167 +684d3ce9eea3213033a2cca5356b2b5d +b52b25eb615173a4ba074f691427bb35 +599f704acf2abbcf3b1e8b978ef701b7 +113425ac426efc2ea5f51d82d5efaa52 +56d3ed71c7035fa73abf6d488274f721 +0d91da350339273c181e3662b1ba0ae9 +1c7d30423c7a2521318760a3038e9f18 +a9efdd7ee319566b0bc95690e0df4375 +081e2680703e5a41e8d4fb8ec7d243fb +9bb75797308d7685679744bfb81cd0b4 +76fef3be9ccc2b09a26b777aaf6eaaa4 +44236eb3b5ea1809444043fefe0f71fe +fe5b559067d97f48935afc11f16084a2 +96560e106f5964516df0198e55190f1c +717ea6d9f4aae40a2cff02c01cc35c80 +c293fda02e48ba48f616801b79b4aa08 +e161ebb8d11c44e50b110672e72f6710 +5c9cd6edac4a12a09b62a9e9f52e1a42 +174561f02a85e7869a1c1b2337dbb2f4 +4481eadb2a89ec677f8f6931886c9467 +c121f1c4e50c1339e4669269fd0d5702 +2d3583ed24a9acdd2c0987dc46275a21 +d8f7edd92eb099d4ca3f70441839a08c +14af38740b09ad336aa16da61e89ce7d +48698b5e61811440f35418dcb927a468 +1620fceead660639db2852f8d306186e +73ed07375433d0eb63d321aa0ecd99d6 +87cc3193223589bffc5d787a2ce81285 +4e845175c8aa2e1e10847d9cda5be10f +2acd324d36d5c8e0ab819a7f7397fcba +b7177a93ae1ce9f78cf8a5c49ced5d33 +bbbb4483163c458cb05b278f5684cc29 +3771702d49d9ab165fdae0c4051120d0 +a265c33d49457a028efe571fe6d0179a +80931cff5537513465338f6bae170362 +4ce26cc523290847505a00a6a024f11a +24eb2fdfa4591ddd57e8fd412bd3b5ca +e0095383f92fa86c17a12723afea3190 +b928337175f5e1c8433b1706f64e7c36 +ef13b2a562c2cfa3a09f9b0ff88299b0 +822e3ffa457afc507cc37a842ad4a74c +61b427bfce25becb065c85c79e7641e3 +ef7ae305934b3a3239af000bc34fe57c +5d6a1250dc9f419404f32c15eecbced5 +a5486ce271411536c1f4ca15c54237d2 +f0dc52da862e67597d33ba7011d31274 +0abf69460b147bd7d685d8a1421ee590 +839823ace6b2fc16e6c32286d3181cd2 +443990cf5a9b1f88f48073f83809b23f +5fa15e930a19022f2d61f9a5daccb218 +771828d8137a18bea72dd4bb3586f455 +f9868c27574acb213232a481ee6e600d +593927f9ffba2045943ec6937e11bdc8 +3f8c1e3cb136eac4baae878e16143fa1 +65a5d3d6a5300f7d5f50c5894cc97f3f +aac5fa0aaf9708cc532356894dd87f01 +291e0337118caea659a8e3aa060adef0 +dbae1b62149064be3add3d1a7ea6c299 +b53caa02b3b0ff4f093b2491f5890a23 +8a7fcddf73aa3b950a98bd4e2389d4ea +c7a0b5800a88ad9b079659c57f5d0cc8 +eb1bf829c4118f96db8e075041cde281 +54875efc69792ba78bff55d4b56f004a +fd66a91527f80c213ff2b1e77fba356b +2d98b918abe6fe1e9d18c190d332362f +8d4f2afe18b23d053b80a07acffe6496 +743e8bf8a55d31a269afc4b740b276a5 +04f887f3a5a68ac6f31b11fd40052dd7 +e04340d58819b5e5ee89edc6aa3c197e +6e466f7fd52b3a49fb69ec4f30e1c69d +748a36d418254c62041a8b8f1a659507 +643775c2bedb87a710fb792f25d58105 +e087bd9094c15257254e383f45f332a2 +0c3aa62047551efa4eeb90ad7d0e1f5f +8e27fc4e3fdeeb8bcd025bbf124a1330 +144e71642d0ee90f13ddd96e07f89be8 +a6f0742d1269377e0c47d72986cc096f +80bdea00e3a263e4acc9ab7336979c14 +c6e8e413404dd1063a04c1cc0177dd93 +16c71b7369186e638829a779da9d7af7 +7a86dd8c7d60da498a11fabfbdd913fe +4876144a7370d8ae75c8212313c493cb +6d5fafbcbf58d1afe71462239e487958 +77e1d6ffcf139b2df44a63a27f35c9e9 +3faf0bf0e4e25a50e64f2e42db25d3a3 +f828cef21e52c67d3b6770da25f062ee +11fd050b70b0ced40a361385148a222e +a1e0428122ba95351ab00a01440ac979 +bf570cf4e3d28843117ab36fa3b47e1e +21c7cd5e6d94af1675b18756075c8398 +8f05538363c469c6dbf09e017fbb8175 +31ee07a0b8c0184567123eef438c9dc2 +a9aab9dbdadc26f0109770184f5211c6 +f476fbef5701070823a26c231ed1c508 +53216c313d75cc03a8c30f9142b13a98 +4c24576e20f873866b73992dc92c2de6 +adde637d0a762bbca0be13f7b51760cc +ce3703b38d2a40af3f35bdf91ae80a4d +6165c5501a656bd868d429a07d372a13 +f761e07a4fa1a56064d436db355b8a71 +83ee362dce073d601f1017fe9c291044 +638f20f8712b6cedbbb7bd32736c5a04 +0e001483bee7844a3e1b7da9ff6234bb +27185336f4c25d4f8f905ef550960245 +a3128cd4cf0d68717350fc7aec77afd8 +93e231ac457ecd41a8de36826f6cd79a +74c86bfa0da98d735e9fe86722a1da52 +540f481f84d2acb2bae64a889f9bbf0b +1ef438e37832bae705ae13b1d22892d0 +206ad7f570277559c1da30bdbc318b53 +d09f24ad549061c069e0b1c992cb0196 +8ee0201c45aa5b48a679bf2686711010 +8c6218af8470d935861003d568fb0d59 +49ee31d299440d085fc8c5399210d201 +12ea29756a972dce8b3a1ffec2622c88 +d9a1ca75ce2d9175ddcca11baef831ab +15d99a33a6c2306eb8ece3167ff29338 +2e997a4c90125536987da50b69f3ec73 +407054c0f51d9ba8785c203a6c2076ee +9a17f86d9d3fedfd4c6091f58fc8b5be +3998f4204e13c050b7597b4b9ca29bbe +cf318c5893ec38c6d128c4412524bce2 +bc042a8a479de6d90dab4bbba3aa390e +1a7cbb293be15d838c1629e02688e066 +0ad0cc183e99af3fc1abc109642a9920 +d6bff48a3786fd0db42a8efb72f653e9 +ecd155c5274a2df186b72de91c43a2b9 +46936f43b7702e132026e590e6062dea +83f42eb575321baf358f6757e02925de +72d5add0c050f68504f56e42bf3f4987 +b7050d0560a4d989018ec535eb124cf3 +0ced37638821de4ea928a57736e81aa1 +a64c47e5a7ca653e4254b53fde9c64e4 +24f38ad6a34d0b8108802c3c72fe9850 +925031670b29d8c2206a645a025cae95 +9f4b4f9cb81952c42d965c6010a8c9eb +1800f10c90621e1cb68474db66c9aeb9 +0a875f1faf8971c7c417c521aed66cf5 +913af47b4e5cce620fd6661f3a602b30 +a3c0469b78561e279c83a88fe8ad609e +b02fd17d34461ab97e92051d65b21816 +08a83640aeb4b2c4b2c812e159cccf26 +2a1a4b56dcdedf7c2ca2e4bb74fd748d +d3ef223e008e7875032505584efd9373 +e86f27eff7f96b6a96b95d2525430c35 +75781c797de704852fadc100d594f271 +a0f04d667c96a3ef1c8d23abe50fcea3 +57127afc1dd458c70bdb3f9d321e6734 +dc2683ff68ef35b2671a32e15c0872fb +af2db43d834681e749881d5ff49aa650 +cc66caf5feec4faea214c815889c13c5 +9cb5bee0d44864376c90eca7b3593dd8 +90db7f00bfd886f343d6c0e2490ddc4b +d15518dbc75f7bdb931f35376e2a2afb +f9e615c2def6a5918da769f9d6e0e159 +50d5820fe29400d2ff1c437e30379b4e +26fa228458935048a8376d616ec42262 +28784e18c77a9386d9756dca945fe9b7 +8faaeee5bf42f4f634cbc223d9df5257 +dc1f61b8d05dbd47c1ce6b104f444c6c +151a35e7099d8a88f54b0872e326c5aa +aeb3c6efc902f701b2216c07be8adac3 +400ce35dcbcc28115051bf820416d3eb +0946f216b96db3262277e98172adc741 +01046e7b7a89bb96b626353ca336a062 +503a526f777e0732f9c8b72a2b655985 +3703803fd6ec2cd3be33c00dcbf11953 +0c9ee8f4281884a4e5da47fc251a84c0 +f2ea4b064e8675c4893b53136505a97d +0c2b21d6a515135f8bfa4c3c74c3b723 +a6c5c220ecad951f40be1fde9d0018e4 +fb95b7173f3f88dd51a2a336ecb0a704 +60f5417a031c7e4f77149c5b43bc934f +c6b351374aa7af59116b8aa1dbb061a4 +856c90e9d0175a62f9abca62d7af938c +fe452264d2b797c991015f0d805eabdd +2482e7f7da93337cdbabb3f4e2cff5b1 +e40d0836142fb40f3913ce4ae7248770 +03609ff573397a7a69de9cc71cd18489 +9b978aa3ea678eb2e299e2d634c4d7a1 +392aa129513df7616a3e189ec880af8c +e76974af77cc86fd958f5c8c1c7d9a42 +f7348a4e852e186f85415abea9fd78b8 +dfac908e9997330ed4b76616dbe6ed96 +be44e878a08bfe62cf4c091ed8027fa2 +b9cf12d691be152cf3ae6b3fea02a6a4 +f868a6b23be8a76e635111c353b07651 +ed0820d4c56afd1ef4f24644aa8d4acb +88e94257298b6c622c3abb5a904e87f8 +8619ca8b240b40eea44c35f15dc69dbd +a24e1dad8effa1abdaf15ccc2f52559b +4a919f7749a7fec59f9a6906ab908516 +9ac820f1357753624f3f0fffc7c960bc +335e09c1f19a4a960dcf96f31838d875 +f95210be6ef5dedeb42ff97b920a3057 diff --git a/vendor/cros-codecs/src/codec/vp8/test_data/vp8-parser-test-0-inter.bin b/vendor/cros-codecs/src/codec/vp8/test_data/vp8-parser-test-0-inter.bin new file mode 100644 index 0000000000000000000000000000000000000000..6fce364dd8a8389d9b61ae79a644128ef9593a9b GIT binary patch literal 554 zcmV+_0@eLd3;+NS5C9&_1Kh*y?;SAXMaYJxq@0d&?yg38Q&F>1C!qg<02zd)2M_r1r6+}NpkL=N)Eo5ya5QF)oQXw3>n~HK0I~rWyWkz z+XXm4{on;_wF1NIju>S5=X{t$Dq4X1>L3W1R&y36IA1z@e)A9GyQp?pyte(KX50US zVJY;JBYt>H95F?-8mA5%s(Rgp+U2N{%7LtMU7->f`R@HO^WRH{b$~1q1GXOsJbN;U z;jX4ZLrvDZ4V;}eHN~g+>=@D#n~;Q@*phzfx|dT^%Lm$l&|lVvlw`TeGc33i1QX;N zgr&q7PKU$nrKPO@*=g`Ez%6FSGrn_pbD5V sWUZQ@Ye3DAx)#MEl5hYH{cuW1Sf}P3O2Y@~;5i*s}9Hwm^G(gLJr7``oMm0ag5++MHkT z{(AAmnyCZ|{ZZn|WLMf;3>KACc}UzWCiy2)AHvpd+@`LT0{^e4-J!951C5vzY=Ak2 zWfL>4+%ANwsH)_X5E#la=%O1*p1|Ui#(;3(_(749#H;Z}-i!V`J}tnski%l{$NP`q z6L}9(Wcw>+7jGphocoxDvHmWBa~@G)Iw2@Awfzc6%Fb;U){p-51VsIK7tlRcMpIy1R8&{7p(a> z$yapE1C_h%1(}-2n@)1c)pdS<3Eu*6S;Qy=h)-GQFv(`3 zQSU&-ysUHs|7x<-8apgXW~l8LVv|bC2Znoa53pXQ-QgOU(<=9v29v+1juTKsyv{Zk z8y^+3B0kxO+ zmBt4uzeXonQp3mQ*UJD?c0NQ;t4+QCUF!3x6b?oizmqzyh literal 0 HcmV?d00001 diff --git a/vendor/cros-codecs/src/codec/vp9.rs b/vendor/cros-codecs/src/codec/vp9.rs new file mode 100644 index 00000000..d26271ec --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp9.rs @@ -0,0 +1,10 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#![warn(clippy::missing_panics_doc)] +#![warn(clippy::panic)] +#![warn(clippy::unwrap_used)] + +pub mod lookups; +pub mod parser; diff --git a/vendor/cros-codecs/src/codec/vp9/lookups.rs b/vendor/cros-codecs/src/codec/vp9/lookups.rs new file mode 100644 index 00000000..4a189de4 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp9/lookups.rs @@ -0,0 +1,116 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// The DC Quantization lookup table for bit_depth = 8, as per "8.6.1 Dequantization functions" +pub const DC_QLOOKUP: [i16; 256] = [ + 4, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, + 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, + 48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 66, 67, + 68, 69, 70, 70, 71, 72, 73, 74, 74, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 85, 87, + 88, 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 114, 116, 117, + 118, 120, 121, 123, 125, 127, 129, 131, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, + 156, 158, 161, 164, 166, 169, 172, 174, 177, 180, 182, 185, 187, 190, 192, 195, 199, 202, 205, + 208, 211, 214, 217, 220, 223, 226, 230, 233, 237, 240, 243, 247, 250, 253, 257, 261, 265, 269, + 272, 276, 280, 284, 288, 292, 296, 300, 304, 309, 313, 317, 322, 326, 330, 335, 340, 344, 349, + 354, 359, 364, 369, 374, 379, 384, 389, 395, 400, 406, 411, 417, 423, 429, 435, 441, 447, 454, + 461, 467, 475, 482, 489, 497, 505, 513, 522, 530, 539, 549, 559, 569, 579, 590, 602, 614, 626, + 640, 654, 668, 684, 700, 717, 736, 755, 775, 796, 819, 843, 869, 896, 925, 955, 988, 1022, + 1058, 1098, 1139, 1184, 1232, 1282, 1336, +]; + +/// The DC Quantization lookup table for bit_depth = 10, as per "8.6.1 Dequantization functions" +pub const DC_QLOOKUP_10: [i16; 256] = [ + 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, +]; + +/// The DC Quantization lookup table for bit_depth = 12, as per "8.6.1 Dequantization functions" +pub const DC_QLOOKUP_12: [i16; 256] = [ + 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, +]; + +/// The AC Quantization lookup table for bit_depth = 8, as per "8.6.1 Dequantization functions" +pub const AC_QLOOKUP: [i16; 256] = [ + 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, + 140, 142, 144, 146, 148, 150, 152, 155, 158, 161, 164, 167, 170, 173, 176, 179, 182, 185, 188, + 191, 194, 197, 200, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255, 260, + 265, 270, 275, 280, 285, 290, 295, 300, 305, 311, 317, 323, 329, 335, 341, 347, 353, 359, 366, + 373, 380, 387, 394, 401, 408, 416, 424, 432, 440, 448, 456, 465, 474, 483, 492, 501, 510, 520, + 530, 540, 550, 560, 571, 582, 593, 604, 615, 627, 639, 651, 663, 676, 689, 702, 715, 729, 743, + 757, 771, 786, 801, 816, 832, 848, 864, 881, 898, 915, 933, 951, 969, 988, 1007, 1026, 1046, + 1066, 1087, 1108, 1129, 1151, 1173, 1196, 1219, 1243, 1267, 1292, 1317, 1343, 1369, 1396, 1423, + 1451, 1479, 1508, 1537, 1567, 1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828, +]; + +/// The AC Quantization lookup table for bit_depth = 10, as per "8.6.1 Dequantization functions" +pub const AC_QLOOKUP_10: [i16; 256] = [ + 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, +]; + +/// The AC Quantization lookup table for bit_depth = 12, as per "8.6.1 Dequantization functions" +pub const AC_QLOOKUP_12: [i16; 256] = [ + 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, +]; diff --git a/vendor/cros-codecs/src/codec/vp9/parser.rs b/vendor/cros-codecs/src/codec/vp9/parser.rs new file mode 100644 index 00000000..3ae1b669 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp9/parser.rs @@ -0,0 +1,1478 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::bitstream_utils::BitReader; +use crate::codec::vp9::lookups::AC_QLOOKUP; +use crate::codec::vp9::lookups::AC_QLOOKUP_10; +use crate::codec::vp9::lookups::AC_QLOOKUP_12; +use crate::codec::vp9::lookups::DC_QLOOKUP; +use crate::codec::vp9::lookups::DC_QLOOKUP_10; +use crate::codec::vp9::lookups::DC_QLOOKUP_12; + +pub const REFS_PER_FRAME: usize = 3; + +pub const MAX_REF_LF_DELTAS: usize = 4; +pub const MAX_MODE_LF_DELTAS: usize = 2; + +pub const INTRA_FRAME: usize = 0; +pub const LAST_FRAME: usize = 1; +pub const GOLDEN_FRAME: usize = 2; +pub const ALTREF_FRAME: usize = 3; +pub const MAX_REF_FRAMES: usize = 4; + +pub const MAX_SEGMENTS: usize = 8; +pub const SEG_TREE_PROBS: usize = MAX_SEGMENTS - 1; +pub const PREDICTION_PROBS: usize = 3; + +/// Valid segment features values. +#[repr(u8)] +pub enum SegLvl { + AltQ = 0, + AltL = 1, + RefFrame = 2, + LvlSkip = 3, +} +pub const SEG_LVL_MAX: usize = 4; + +pub const MAX_LOOP_FILTER: u32 = 63; + +pub const REF_FRAMES_LOG2: usize = 3; +pub const REF_FRAMES: usize = 1 << REF_FRAMES_LOG2; + +pub const SUPERFRAME_MARKER: u32 = 0x06; +pub const MAX_FRAMES_IN_SUPERFRAME: usize = 8; + +pub const FRAME_MARKER: u32 = 0x02; +pub const SYNC_CODE: u32 = 0x498342; + +pub const MIN_TILE_WIDTH_B64: u32 = 4; +pub const MAX_TILE_WIDTH_B64: u32 = 64; + +/// The number of pictures in the DPB +pub const NUM_REF_FRAMES: usize = 8; + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum InterpolationFilter { + #[default] + EightTap = 0, + EightTapSmooth = 1, + EightTapSharp = 2, + Bilinear = 3, + Switchable = 4, +} + +impl TryFrom for InterpolationFilter { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(InterpolationFilter::EightTap), + 1 => Ok(InterpolationFilter::EightTapSmooth), + 2 => Ok(InterpolationFilter::EightTapSharp), + 3 => Ok(InterpolationFilter::Bilinear), + 4 => Ok(InterpolationFilter::Switchable), + _ => Err(format!("Invalid InterpolationFilter {}", value)), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ReferenceFrameType { + Intra = 0, + Last = 1, + Golden = 2, + AltRef = 3, +} + +impl TryFrom for ReferenceFrameType { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ReferenceFrameType::Intra), + 1 => Ok(ReferenceFrameType::Last), + 2 => Ok(ReferenceFrameType::Golden), + 3 => Ok(ReferenceFrameType::AltRef), + _ => Err(format!("Invalid ReferenceFrameType {}", value)), + } + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum FrameType { + #[default] + KeyFrame = 0, + InterFrame = 1, +} + +impl TryFrom for FrameType { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(FrameType::KeyFrame), + 1 => Ok(FrameType::InterFrame), + _ => Err(format!("Invalid FrameType {}", value)), + } + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum Profile { + #[default] + Profile0 = 0, + Profile1 = 1, + Profile2 = 2, + Profile3 = 3, +} + +impl TryFrom for Profile { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(Profile::Profile0), + 1 => Ok(Profile::Profile1), + 2 => Ok(Profile::Profile2), + 3 => Ok(Profile::Profile3), + _ => Err(format!("Invalid Profile {}", value)), + } + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum BitDepth { + #[default] + Depth8 = 8, + Depth10 = 10, + Depth12 = 12, +} + +impl TryFrom for BitDepth { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 8 => Ok(BitDepth::Depth8), + 10 => Ok(BitDepth::Depth10), + 12 => Ok(BitDepth::Depth12), + _ => Err(format!("Invalid BitDepth {}", value)), + } + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum ColorSpace { + #[default] + Unknown = 0, + Bt601 = 1, + Bt709 = 2, + Smpte170 = 3, + Smpte240 = 4, + Bt2020 = 5, + Reserved2 = 6, + CsSrgb = 7, +} + +impl TryFrom for ColorSpace { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ColorSpace::Unknown), + 1 => Ok(ColorSpace::Bt601), + 2 => Ok(ColorSpace::Bt709), + 3 => Ok(ColorSpace::Smpte170), + 4 => Ok(ColorSpace::Smpte240), + 5 => Ok(ColorSpace::Bt2020), + 6 => Ok(ColorSpace::Reserved2), + 7 => Ok(ColorSpace::CsSrgb), + _ => Err(format!("Invalid ColorSpace {}", value)), + } + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum ColorRange { + #[default] + StudioSwing = 0, + FullSwing = 1, +} + +impl TryFrom for ColorRange { + type Error = String; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ColorRange::StudioSwing), + 1 => Ok(ColorRange::FullSwing), + _ => Err(format!("Invalid ColorRange {}", value)), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct LoopFilterParams { + /// Indicates the loop filter strength. + pub level: u8, + /// Indicates the sharpness level. The loop filter level and loop + /// filter_sharpness together determine when a block edge is filtered, and + /// by how much the filtering can change the sample values. + pub sharpness: u8, + /// If set, means that the filter level depends on the mode and reference + /// frame used to predict a block. If unset, means that the filter level + /// does not depend on the mode and reference frame. + pub delta_enabled: bool, + /// If set, means that the bitstream contains additional syntax elements + /// that specify which mode and reference frame deltas are to be updated. If + /// unset, means that these syntax elements are not present. + pub delta_update: bool, + /// If set, means that the bitstream contains additional syntax elements + /// that specify which mode and reference frame deltas are to be updated. If + /// unset, means that these syntax elements are not present. + pub update_ref_delta: [bool; MAX_REF_LF_DELTAS], + /// Contains the adjustment needed for the filter level based on the chosen + /// reference frame. If this syntax element is not present in the bitstream, + /// it maintains its previous value. + pub ref_deltas: [i8; MAX_REF_LF_DELTAS], + /// If set, means that the bitstream contains the syntax element + /// loop_filter_mode_deltas. If unset, means that the bitstream does not + /// contain this syntax element. + pub update_mode_delta: [bool; MAX_MODE_LF_DELTAS], + /// Contains the adjustment needed for the filter level based on the chosen + /// mode. If this syntax element is not present in the bitstream, it + /// maintains its previous value. + pub mode_deltas: [i8; MAX_MODE_LF_DELTAS], +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct QuantizationParams { + /// Indicates the base frame qindex. This is used for Y AC coefficients and + /// as the base value for the other quantizers. + pub base_q_idx: u8, + /// Indicates the Y DC quantizer relative to base_q_idx. + pub delta_q_y_dc: i8, + /// Indicates the UV DC quantizer relative to base_q_idx. + pub delta_q_uv_dc: i8, + /// Indicates the UV AC quantizer relative to base_q_idx. + pub delta_q_uv_ac: i8, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SegmentationParams { + /// If set, indicates that this frame makes use of the segmentation tool. + /// If unset, indicates that the frame does not use segmentation. + pub enabled: bool, + /// If set, indicates that the segmentation map should be updated during + /// the decoding of this frame. If unset, means that the segmentation map + /// from the previous frame is used. + pub update_map: bool, + /// Specify the probability values to be used when decoding segment_id. + pub tree_probs: [u8; SEG_TREE_PROBS], + /// Specify the probability values to be used when decoding seg_id_predicted. + pub pred_probs: [u8; PREDICTION_PROBS], + /// If set, indicates that the updates to the segmentation map are coded + /// relative to the existing segmentation map. If unset, + /// indicates that the new segmentation map is coded without + /// reference to the existing segmentation map. + pub temporal_update: bool, + /// If set, indicates that new parameters are about to be specified for each + /// segment. If unset, indicates that the segmentation parameters should + /// keep their existing values. + pub update_data: bool, + /// If unset, indicates that the segmentation parameters represent + /// adjustments relative to the standard values. If set, indicates that the + /// segmentation parameters represent the actual values to be used. + pub abs_or_delta_update: bool, + /// If unset, indicates that the corresponding feature is unused and has + /// value equal to 0. if set, indicates that the feature value is coded in + /// the bitstream. + pub feature_enabled: [[bool; SEG_LVL_MAX]; MAX_SEGMENTS], + /// Specifies the magnitude of the feature data for a segment feature. + pub feature_data: [[i16; SEG_LVL_MAX]; MAX_SEGMENTS], +} + +impl SegmentationParams { + /// Returns whether `feature` is enabled for `segment_id`. + fn is_feature_enabled(&self, segment_id: u8, feature: SegLvl) -> bool { + self.feature_enabled[segment_id as usize][feature as usize] + } + + /// An implementation of seg_feature_active as per "6.4.9 Segmentation feature active syntax" + fn is_feature_active(&self, segment_id: u8, feature: SegLvl) -> bool { + self.enabled && self.is_feature_enabled(segment_id, feature) + } + + /// Returns the data for `feature` on `segment_id`. + fn feature_data(&self, segment_id: u8, feature: SegLvl) -> i16 { + self.feature_data[segment_id as usize][feature as usize] + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Segmentation { + /// Loop filter level + pub lvl_lookup: [[u8; MAX_MODE_LF_DELTAS]; MAX_REF_FRAMES], + + /// AC quant scale for luma component + pub luma_ac_quant_scale: i16, + /// DC quant scale for luma component + pub luma_dc_quant_scale: i16, + /// AC quant scale for chroma component + pub chroma_ac_quant_scale: i16, + /// DC quant scale for chroma component + pub chroma_dc_quant_scale: i16, + + /// Whether the alternate reference frame segment feature is enabled (SEG_LVL_REF_FRAME) + pub reference_frame_enabled: bool, + /// The feature data for the reference frame featire + pub reference_frame: i16, + /// Whether the skip segment feature is enabled (SEG_LVL_SKIP) + pub reference_skip_enabled: bool, +} + +impl Segmentation { + /// Update the state of the segmentation parameters after seeing a frame + pub fn update_segmentation(segmentation: &mut [Segmentation; MAX_SEGMENTS], hdr: &Header) { + let lf = &hdr.lf; + let seg = &hdr.seg; + + let n_shift = lf.level >> 5; + + for segment_id in 0..MAX_SEGMENTS as u8 { + let luma_dc_quant_scale = hdr.get_dc_quant(segment_id, true); + let luma_ac_quant_scale = hdr.get_ac_quant(segment_id, true); + let chroma_dc_quant_scale = hdr.get_dc_quant(segment_id, false); + let chroma_ac_quant_scale = hdr.get_ac_quant(segment_id, false); + + let mut lvl_lookup: [[u8; MAX_MODE_LF_DELTAS]; MAX_REF_FRAMES]; + + if lf.level == 0 { + lvl_lookup = Default::default() + } else { + let mut lvl_seg = i32::from(lf.level); + + // 8.8.1 Loop filter frame init process + if hdr.seg.is_feature_active(segment_id, SegLvl::AltL) { + if seg.abs_or_delta_update { + lvl_seg = i32::from(seg.feature_data(segment_id, SegLvl::AltL)); + } else { + lvl_seg += i32::from(seg.feature_data(segment_id, SegLvl::AltL)); + } + } + + let lvl_seg = lvl_seg.clamp(0, MAX_LOOP_FILTER as i32) as u8; + + if !lf.delta_enabled { + lvl_lookup = [[lvl_seg; MAX_MODE_LF_DELTAS]; MAX_REF_FRAMES] + } else { + let intra_delta = lf.ref_deltas[INTRA_FRAME] as i32; + let mut intra_lvl = lvl_seg as i32 + (intra_delta << n_shift); + + lvl_lookup = segmentation[segment_id as usize].lvl_lookup; + lvl_lookup[INTRA_FRAME][0] = intra_lvl.clamp(0, MAX_LOOP_FILTER as i32) as u8; + + // Note, this array has the [0] element unspecified/unused in + // VP9. Confusing, but we do start to index from 1. + #[allow(clippy::needless_range_loop)] + for ref_ in LAST_FRAME..MAX_REF_FRAMES { + for mode in 0..MAX_MODE_LF_DELTAS { + let ref_delta = lf.ref_deltas[ref_] as i32; + let mode_delta = lf.mode_deltas[mode] as i32; + + intra_lvl = + lvl_seg as i32 + (ref_delta << n_shift) + (mode_delta << n_shift); + + lvl_lookup[ref_][mode] = + intra_lvl.clamp(0, MAX_LOOP_FILTER as i32) as u8; + } + } + } + } + + segmentation[usize::from(segment_id)] = Segmentation { + lvl_lookup, + luma_ac_quant_scale, + luma_dc_quant_scale, + chroma_ac_quant_scale, + chroma_dc_quant_scale, + reference_frame_enabled: seg.is_feature_enabled(segment_id, SegLvl::RefFrame), + reference_frame: seg.feature_data(segment_id, SegLvl::RefFrame), + reference_skip_enabled: seg.is_feature_enabled(segment_id, SegLvl::LvlSkip), + } + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +struct FrameSize { + width: u32, + height: u32, +} + +pub struct Frame<'a> { + /// The bitstream data for this frame. + bitstream: &'a [u8], + /// The frame header. + pub header: Header, + /// The offset into T + offset: usize, + /// The size of the data in T + size: usize, +} + +impl<'a> Frame<'a> { + pub fn new(bitstream: &'a [u8], header: Header, offset: usize, size: usize) -> Self { + Self { bitstream, header, offset, size } + } +} + +impl<'a> AsRef<[u8]> for Frame<'a> { + fn as_ref(&self) -> &[u8] { + let data = self.bitstream; + &data[self.offset..self.offset + self.size] + } +} + +/// A VP9 frame header. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Header { + /// A subset of syntax, semantics and algorithms defined in a part. + pub profile: Profile, + /// The bit depth of the frame. + pub bit_depth: BitDepth, + /// Specifies the chroma subsampling format. + pub subsampling_x: bool, + /// Specifies the chroma subsampling format. + pub subsampling_y: bool, + /// Specifies the color space of the stream. + pub color_space: ColorSpace, + /// Specifies the black level and range of the luma and chroma signals as + /// specified in Rec. ITU-R BT.709-6 and Rec. ITU-R BT.2020-2 + pub color_range: ColorRange, + /// Indicates the frame indexed by frame_to_show_map_idx is to be displayed. + /// If unset, indicates that further processing is required. + pub show_existing_frame: bool, + /// Specifies the frame to be displayed. It is only available if + /// show_existing_frame is set. + pub frame_to_show_map_idx: u8, + /// Indicates whether a frame is a key frame. + pub frame_type: FrameType, + /// Whether this frame should be displayed. + pub show_frame: bool, + /// Whether error resilient mode is enabled. + pub error_resilient_mode: bool, + /// The width of the frame in pixels. + pub width: u32, + /// The height of the frame in pixels. + pub height: u32, + /// If unset, means that the render width and height are inferred from the + /// frame width and height. If set, means that the render width and height + /// are explicitly coded in the bitstream. + pub render_and_frame_size_different: bool, + /// The render width of the frame in pixels. + pub render_width: u32, + /// The render height of the frame in pixels. + pub render_height: u32, + /// If set, indicates that this frame is an intra-only frame. If unset, + /// indicates that this frame is a inter frame. + pub intra_only: bool, + /// Specifies whether the frame context should be reset to default values. + pub reset_frame_context: u8, + /// Contains a bitmask that specifies which reference frame slots will be + /// updated with the current frame after it is decoded. + pub refresh_frame_flags: u8, + /// Specifies which reference frames are used by inter frames. It is a + /// requirement of bitstream conformance that the selected reference frames + /// match the current frame in bit depth, profile, chroma subsampling, and + /// color space. + pub ref_frame_idx: [u8; REFS_PER_FRAME], + /// Specifies the intended direction of the motion vector in time for each + /// reference frame. A sign bias equal to 0 indicates that the reference + /// frame is a backwards reference; a sign bias equal to 1 indicates that + /// the reference frame is a forwards reference + pub ref_frame_sign_bias: [u8; 4], + /// If unset, specifies that motion vectors are specified to quarter pel + /// precision. If set, specifies that motion vectors are specified to eighth + /// pel precision. + pub allow_high_precision_mv: bool, + /// The interpolation filter parameters. + pub interpolation_filter: InterpolationFilter, + /// If set, indicates that the probabilities computed for this frame (after + /// adapting to the observed frequencies if adaption is enabled) should be + /// stored for reference by future frames. If unset, indicates that the + /// probabilities should be discarded at the end of the frame. + pub refresh_frame_context: bool, + /// Whether parallel decoding mode is enabled. + pub frame_parallel_decoding_mode: bool, + /// Indicates the frame context to use. + pub frame_context_idx: u8, + /// The loop filter parameters + pub lf: LoopFilterParams, + /// The quantization parameters. + pub quant: QuantizationParams, + /// The segmentation parameters + pub seg: SegmentationParams, + /// Specifies the base 2 logarithm of the width of each tile (where the + /// width is measured in units of 8x8 blocks). It is a requirement of + /// bitstream conformance that tile_cols_log2 is less than or equal to 6. + pub tile_cols_log2: u8, + /// Specifies the base 2 logarithm of the height of each tile (where the + /// height is measured in units of 8x8 blocks). + pub tile_rows_log2: u8, + /// Computed from the syntax elements. If set, indicates that the frame is + /// coded using a special 4x4 transform designed for encoding frames that + /// are bit-identical with the original frames. + pub lossless: bool, + /// Indicates the size of the compressed header in bytes. + pub header_size_in_bytes: u16, + /// Indicates the size of the uncompressed header in bytes. + pub uncompressed_header_size_in_bytes: u16, +} + +impl Header { + /// An implementation of get_qindex as per "8.6.1 Dequantization functions" + fn get_qindex(&self, segment_id: u8) -> u8 { + let base_q_idx = self.quant.base_q_idx; + + if self.seg.is_feature_active(segment_id, SegLvl::AltQ) { + let mut data = self.seg.feature_data(segment_id, SegLvl::AltQ) as i32; + + if !self.seg.abs_or_delta_update { + data += base_q_idx as i32; + } + + data.clamp(0, 255) as u8 + } else { + base_q_idx + } + } + + /// An implementation of get_dc_quant as per "8.6.1 Dequantization functions" + fn get_dc_quant(&self, segment_id: u8, luma: bool) -> i16 { + let delta_q_dc = + if luma { self.quant.delta_q_y_dc } else { self.quant.delta_q_uv_dc } as i32; + let qindex = self.get_qindex(segment_id); + let q_table_idx = (qindex as i32 + delta_q_dc).clamp(0, 255) as u8; + + let table = match self.bit_depth { + BitDepth::Depth8 => &DC_QLOOKUP, + BitDepth::Depth10 => &DC_QLOOKUP_10, + BitDepth::Depth12 => &DC_QLOOKUP_12, + }; + + table[q_table_idx as usize] + } + + /// An implementation of get_ac_quant as per "8.6.1 Dequantization functions" + fn get_ac_quant(&self, segment_id: u8, luma: bool) -> i16 { + let delta_q_ac = if luma { 0 } else { self.quant.delta_q_uv_ac } as i32; + let qindex = self.get_qindex(segment_id); + let q_table_idx = (qindex as i32 + delta_q_ac).clamp(0, 255) as u8; + + let table = match self.bit_depth { + BitDepth::Depth8 => &AC_QLOOKUP, + BitDepth::Depth10 => &AC_QLOOKUP_10, + BitDepth::Depth12 => &AC_QLOOKUP_12, + }; + + table[q_table_idx as usize] + } +} + +/// The VP9 superframe header as per Annex B, B.2.1, B.2.2 +struct SuperframeHeader { + /// Indicates the number of frames within this superframe. NOTE - It is + /// legal for a superframe to contain just a single frame and have NumFrames + /// equal to 1. + frames_in_superframe: u32, + /// Specifies the size in bytes of frame number i (zero indexed) within this + /// superframe. + frame_sizes: Vec, +} + +/// A VP9 bitstream parser. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Parser { + bit_depth: BitDepth, + subsampling_x: bool, + subsampling_y: bool, + color_space: ColorSpace, + color_range: ColorRange, + + mi_cols: u32, + mi_rows: u32, + sb64_cols: u32, + sb64_rows: u32, + + lf: LoopFilterParams, + seg: SegmentationParams, + + reference_frame_sz: [FrameSize; REF_FRAMES], +} + +impl Parser { + fn parse_superframe_hdr(resource: impl AsRef<[u8]>) -> Result { + let bitstream = resource.as_ref(); + + // Skip to the end of the chunk. + let mut reader = BitReader::new(&bitstream[bitstream.len() - 1..], false); + + // Try reading a superframe marker. + let marker = reader.read_bits::(3)?; + + if marker != SUPERFRAME_MARKER { + // Not a superframe + return Ok(SuperframeHeader { + frames_in_superframe: 1, + frame_sizes: vec![bitstream.len()], + }); + } + + let bytes_per_framesize = reader.read_bits::(2)? + 1; + let frames_in_superframe = reader.read_bits::(3)? + 1; + + if frames_in_superframe > MAX_FRAMES_IN_SUPERFRAME as u32 { + return Err(format!( + "Broken stream: too many frames in superframe, expected a maximum of {:?}, found {:?}", + MAX_FRAMES_IN_SUPERFRAME, + frames_in_superframe + )); + } + + let sz_index = 2 + frames_in_superframe * bytes_per_framesize; + + let data = resource.as_ref(); + let index_offset = data.len() - sz_index as usize; + let first_byte = data[index_offset]; + let last_byte = *data.last().ok_or_else(|| String::from("superframe header is empty"))?; + + if first_byte != last_byte { + // Also not a superframe, we must pass both tests as per the specification. + return Ok(SuperframeHeader { + frames_in_superframe: 1, + frame_sizes: vec![bitstream.len()], + }); + } + + let mut frame_sizes = vec![]; + let mut reader = BitReader::new(&bitstream[index_offset..], false); + + // Skip the superframe header. + let _ = reader.read_bits::(8)?; + + for _ in 0..frames_in_superframe { + let mut frame_size = 0; + + for j in 0..bytes_per_framesize { + frame_size |= reader.read_bits::(8)? << (j * 8); + } + + frame_sizes.push(frame_size as usize); + } + + Ok(SuperframeHeader { frames_in_superframe, frame_sizes }) + } + + fn read_signed_8(r: &mut BitReader, nbits: u8) -> Result { + let value = r.read_bits::(nbits as usize)?; + let negative = r.read_bit()?; + + if negative { + Ok(-(value as i8)) + } else { + Ok(value as i8) + } + } + + fn parse_frame_marker(r: &mut BitReader) -> Result<(), String> { + let marker = r.read_bits::(2)?; + + if marker != FRAME_MARKER { + return Err(format!("Broken stream: expected frame marker, found {:?}", marker)); + } + + Ok(()) + } + + fn parse_profile(r: &mut BitReader) -> Result { + let low = r.read_bits::(1)?; + let high = r.read_bits::(1)?; + + let profile = (high << 1) | low; + + if profile == 3 { + // Skip the reserved bit + let _ = r.read_bit()?; + } + + Profile::try_from(profile) + } + + fn parse_frame_sync_code(r: &mut BitReader) -> Result<(), String> { + let sync_code = r.read_bits::(24)?; + + if sync_code != SYNC_CODE { + return Err(format!( + "Broken stream: expected sync code == {:?}, found {:?}", + SYNC_CODE, sync_code + )); + } + + Ok(()) + } + + fn parse_color_config(&mut self, r: &mut BitReader, hdr: &mut Header) -> Result<(), String> { + if matches!(hdr.profile, Profile::Profile2 | Profile::Profile3) { + let ten_or_twelve_bit = r.read_bit()?; + if ten_or_twelve_bit { + hdr.bit_depth = BitDepth::Depth12; + } else { + hdr.bit_depth = BitDepth::Depth10 + } + } else { + hdr.bit_depth = BitDepth::Depth8; + } + + let color_space = r.read_bits::(3)?; + hdr.color_space = ColorSpace::try_from(color_space)?; + + if !matches!(hdr.color_space, ColorSpace::CsSrgb) { + let color_range = r.read_bits::(1)?; + + hdr.color_range = ColorRange::try_from(color_range)?; + + if matches!(hdr.profile, Profile::Profile1 | Profile::Profile3) { + hdr.subsampling_x = r.read_bit()?; + hdr.subsampling_y = r.read_bit()?; + + // Skip the reserved bit + let _ = r.read_bit()?; + } else { + hdr.subsampling_x = true; + hdr.subsampling_y = true; + } + } else { + hdr.color_range = ColorRange::FullSwing; + if matches!(hdr.profile, Profile::Profile1 | Profile::Profile3) { + hdr.subsampling_x = false; + hdr.subsampling_y = false; + + // Skip the reserved bit + let _ = r.read_bit()?; + } + } + + self.bit_depth = hdr.bit_depth; + self.color_space = hdr.color_space; + self.subsampling_x = hdr.subsampling_x; + self.subsampling_y = hdr.subsampling_y; + self.color_range = hdr.color_range; + + Ok(()) + } + + fn compute_image_size(&mut self, width: u32, height: u32) { + self.mi_cols = (width + 7) >> 3; + self.mi_rows = (height + 7) >> 3; + self.sb64_cols = (self.mi_cols + 7) >> 3; + self.sb64_rows = (self.mi_rows + 7) >> 3; + } + + fn parse_frame_size(&mut self, r: &mut BitReader, hdr: &mut Header) -> Result<(), String> { + hdr.width = r.read_bits::(16)? + 1; + hdr.height = r.read_bits::(16)? + 1; + self.compute_image_size(hdr.width, hdr.height); + Ok(()) + } + + fn parse_render_size(r: &mut BitReader, hdr: &mut Header) -> Result<(), String> { + hdr.render_and_frame_size_different = r.read_bit()?; + if hdr.render_and_frame_size_different { + hdr.render_width = r.read_bits::(16)? + 1; + hdr.render_height = r.read_bits::(16)? + 1; + } else { + hdr.render_width = hdr.width; + hdr.render_height = hdr.height; + } + + Ok(()) + } + + fn parse_frame_size_with_refs( + &mut self, + r: &mut BitReader, + hdr: &mut Header, + ) -> Result<(), String> { + let mut found_ref = false; + + for i in 0..REFS_PER_FRAME { + found_ref = r.read_bit()?; + + if found_ref { + let idx = hdr.ref_frame_idx[i] as usize; + hdr.width = self.reference_frame_sz[idx].width; + hdr.height = self.reference_frame_sz[idx].height; + break; + } + } + + if !found_ref { + self.parse_frame_size(r, hdr)?; + } else { + self.compute_image_size(hdr.width, hdr.height) + } + + Self::parse_render_size(r, hdr) + } + + fn read_interpolation_filter(r: &mut BitReader) -> Result { + const LITERAL_TO_TYPE: [InterpolationFilter; 4] = [ + InterpolationFilter::EightTapSmooth, + InterpolationFilter::EightTap, + InterpolationFilter::EightTapSharp, + InterpolationFilter::Bilinear, + ]; + + let is_filter_switchable = r.read_bit()?; + + Ok(if is_filter_switchable { + InterpolationFilter::Switchable + } else { + let raw_interpolation_filter = r.read_bits::(2)?; + LITERAL_TO_TYPE[raw_interpolation_filter as usize] + }) + } + + fn setup_past_independence(&mut self, hdr: &mut Header) { + self.seg.feature_enabled = Default::default(); + self.seg.feature_data = Default::default(); + self.seg.abs_or_delta_update = false; + + self.lf.delta_enabled = true; + self.lf.ref_deltas[ReferenceFrameType::Intra as usize] = 1; + self.lf.ref_deltas[ReferenceFrameType::Last as usize] = 0; + self.lf.ref_deltas[ReferenceFrameType::Golden as usize] = -1; + self.lf.ref_deltas[ReferenceFrameType::AltRef as usize] = -1; + + self.lf.mode_deltas = Default::default(); + hdr.ref_frame_sign_bias = Default::default(); + } + + fn parse_loop_filter_params( + r: &mut BitReader, + lf: &mut LoopFilterParams, + ) -> Result<(), String> { + lf.level = r.read_bits::(6)?; + lf.sharpness = r.read_bits::(3)?; + lf.delta_enabled = r.read_bit()?; + + if lf.delta_enabled { + lf.delta_update = r.read_bit()?; + if lf.delta_update { + for i in 0..MAX_REF_LF_DELTAS { + lf.update_ref_delta[i] = r.read_bit()?; + if lf.update_ref_delta[i] { + lf.ref_deltas[i] = Self::read_signed_8(r, 6)?; + } + } + + for i in 0..MAX_MODE_LF_DELTAS { + lf.update_mode_delta[i] = r.read_bit()?; + if lf.update_mode_delta[i] { + lf.mode_deltas[i] = Self::read_signed_8(r, 6)?; + } + } + } + } + + Ok(()) + } + + fn read_delta_q(r: &mut BitReader, value: &mut i8) -> Result<(), String> { + let delta_coded = r.read_bit()?; + + if delta_coded { + *value = Self::read_signed_8(r, 4)?; + } else { + *value = 0; + } + + Ok(()) + } + + fn parse_quantization_params(r: &mut BitReader, hdr: &mut Header) -> Result<(), String> { + let quant = &mut hdr.quant; + + quant.base_q_idx = r.read_bits::(8)?; + + Self::read_delta_q(r, &mut quant.delta_q_y_dc)?; + Self::read_delta_q(r, &mut quant.delta_q_uv_dc)?; + Self::read_delta_q(r, &mut quant.delta_q_uv_ac)?; + + hdr.lossless = quant.base_q_idx == 0 + && quant.delta_q_y_dc == 0 + && quant.delta_q_uv_dc == 0 + && quant.delta_q_uv_ac == 0; + + Ok(()) + } + + fn read_prob(r: &mut BitReader) -> Result { + let prob_coded = r.read_bit()?; + + let prob = if prob_coded { r.read_bits::(8)? } else { 255 }; + + Ok(prob) + } + + fn parse_segmentation_params( + r: &mut BitReader, + seg: &mut SegmentationParams, + ) -> Result<(), String> { + const SEGMENTATION_FEATURE_BITS: [u8; SEG_LVL_MAX] = [8, 6, 2, 0]; + const SEGMENTATION_FEATURE_SIGNED: [bool; SEG_LVL_MAX] = [true, true, false, false]; + + seg.update_map = false; + seg.update_data = false; + + seg.enabled = r.read_bit()?; + + if !seg.enabled { + return Ok(()); + } + + seg.update_map = r.read_bit()?; + + if seg.update_map { + for i in 0..SEG_TREE_PROBS { + seg.tree_probs[i] = Self::read_prob(r)?; + } + + seg.temporal_update = r.read_bit()?; + + for i in 0..PREDICTION_PROBS { + seg.pred_probs[i] = if seg.temporal_update { Self::read_prob(r)? } else { 255 }; + } + } + + seg.update_data = r.read_bit()?; + + if seg.update_data { + seg.abs_or_delta_update = r.read_bit()?; + for i in 0..MAX_SEGMENTS { + for j in 0..SEG_LVL_MAX { + seg.feature_enabled[i][j] = r.read_bit()?; + if seg.feature_enabled[i][j] { + let bits_to_read = SEGMENTATION_FEATURE_BITS[j]; + let mut feature_value = r.read_bits_signed::(bits_to_read as usize)?; + + if SEGMENTATION_FEATURE_SIGNED[j] { + let feature_sign = r.read_bit()?; + + if feature_sign { + feature_value = -feature_value; + } + } + + seg.feature_data[i][j] = feature_value; + } + } + } + } + + Ok(()) + } + + fn calc_min_log2_tile_cols(sb64_cols: u32) -> u8 { + let mut min_log2 = 0; + + while (MAX_TILE_WIDTH_B64 << min_log2) < sb64_cols { + min_log2 += 1; + } + + min_log2 + } + + fn calc_max_log2_tile_cols(sb64_cols: u32) -> u8 { + let mut max_log2 = 1; + + while (sb64_cols >> max_log2) >= MIN_TILE_WIDTH_B64 { + max_log2 += 1; + } + + max_log2 - 1 + } + + fn parse_tile_info(&self, r: &mut BitReader, hdr: &mut Header) -> Result<(), String> { + let max_log2_tile_cols = Self::calc_max_log2_tile_cols(self.sb64_cols); + + hdr.tile_cols_log2 = Self::calc_min_log2_tile_cols(self.sb64_cols); + + while hdr.tile_cols_log2 < max_log2_tile_cols { + let increment_tile_cols_log2 = r.read_bit()?; + + if increment_tile_cols_log2 { + hdr.tile_cols_log2 += 1; + } else { + break; + } + } + + hdr.tile_rows_log2 = r.read_bits::(1)?; + + if hdr.tile_rows_log2 > 0 { + let increment_tile_rows_log2 = r.read_bit()?; + hdr.tile_rows_log2 += increment_tile_rows_log2 as u8; + } + + Ok(()) + } + + fn parse_frame_header( + &mut self, + resource: impl AsRef<[u8]>, + offset: usize, + ) -> Result { + let data = &resource.as_ref()[offset..]; + let mut r = BitReader::new(data, false); + let mut hdr = Header::default(); + + Self::parse_frame_marker(&mut r)?; + hdr.profile = Self::parse_profile(&mut r)?; + + hdr.show_existing_frame = r.read_bit()?; + + if hdr.show_existing_frame { + hdr.frame_to_show_map_idx = r.read_bits::(3)?; + return Ok(hdr); + } + + hdr.frame_type = FrameType::try_from(r.read_bits::(1)?)?; + + hdr.show_frame = r.read_bit()?; + hdr.error_resilient_mode = r.read_bit()?; + + let frame_is_intra; + + if matches!(hdr.frame_type, FrameType::KeyFrame) { + Self::parse_frame_sync_code(&mut r)?; + self.parse_color_config(&mut r, &mut hdr)?; + self.parse_frame_size(&mut r, &mut hdr)?; + Self::parse_render_size(&mut r, &mut hdr)?; + hdr.refresh_frame_flags = 0xff; + frame_is_intra = true; + } else { + if !hdr.show_frame { + hdr.intra_only = r.read_bit()?; + } + + frame_is_intra = hdr.intra_only; + + if !hdr.error_resilient_mode { + hdr.reset_frame_context = r.read_bits::(2)?; + } else { + hdr.reset_frame_context = 0; + } + + if hdr.intra_only { + Self::parse_frame_sync_code(&mut r)?; + + if !matches!(hdr.profile, Profile::Profile0) { + self.parse_color_config(&mut r, &mut hdr)?; + } else { + hdr.color_space = ColorSpace::Bt601; + hdr.subsampling_x = true; + hdr.subsampling_y = true; + hdr.bit_depth = BitDepth::Depth8; + + self.color_space = hdr.color_space; + self.subsampling_x = hdr.subsampling_x; + self.subsampling_y = hdr.subsampling_y; + self.bit_depth = hdr.bit_depth; + } + + hdr.refresh_frame_flags = r.read_bits::(8)?; + self.parse_frame_size(&mut r, &mut hdr)?; + Self::parse_render_size(&mut r, &mut hdr)?; + } else { + // Copy from our cached version + hdr.color_space = self.color_space; + hdr.color_range = self.color_range; + hdr.subsampling_x = self.subsampling_x; + hdr.subsampling_y = self.subsampling_y; + hdr.bit_depth = self.bit_depth; + + hdr.refresh_frame_flags = r.read_bits::(8)?; + + for i in 0..REFS_PER_FRAME { + hdr.ref_frame_idx[i] = r.read_bits::(3)?; + hdr.ref_frame_sign_bias[ReferenceFrameType::Last as usize + i] = + r.read_bits::(1)?; + } + + self.parse_frame_size_with_refs(&mut r, &mut hdr)?; + hdr.allow_high_precision_mv = r.read_bit()?; + hdr.interpolation_filter = Self::read_interpolation_filter(&mut r)?; + } + } + + if !hdr.error_resilient_mode { + hdr.refresh_frame_context = r.read_bit()?; + hdr.frame_parallel_decoding_mode = r.read_bit()?; + } else { + hdr.refresh_frame_context = false; + hdr.frame_parallel_decoding_mode = true; + } + + hdr.frame_context_idx = r.read_bits::(2)?; + + if frame_is_intra || hdr.error_resilient_mode { + self.setup_past_independence(&mut hdr); + } + + Self::parse_loop_filter_params(&mut r, &mut self.lf)?; + Self::parse_quantization_params(&mut r, &mut hdr)?; + Self::parse_segmentation_params(&mut r, &mut self.seg)?; + self.parse_tile_info(&mut r, &mut hdr)?; + + hdr.header_size_in_bytes = r.read_bits::(16)?; + + hdr.lf = self.lf.clone(); + hdr.seg = self.seg.clone(); + + for i in 0..REF_FRAMES { + let flag = 1 << i; + if hdr.refresh_frame_flags & flag != 0 { + self.reference_frame_sz[i].width = hdr.width; + self.reference_frame_sz[i].height = hdr.height; + } + } + + hdr.uncompressed_header_size_in_bytes = (r.position() as u16 + 7) / 8; + + Ok(hdr) + } + + /// Parse a single VP9 frame. + pub fn parse_frame<'a>( + &mut self, + bitstream: &'a [u8], + offset: usize, + size: usize, + ) -> Result, String> { + let header = self.parse_frame_header(bitstream, offset)?; + + Ok(Frame { header, bitstream, offset, size }) + } + + /// Parses VP9 frames from the data in `resource`. This can result in more than one frame if the + /// data passed in contains a VP9 superframe. + pub fn parse_chunk<'a>(&mut self, resource: &'a [u8]) -> Result>, String> { + let superframe_hdr = Parser::parse_superframe_hdr(resource)?; + let mut offset = 0; + + let mut frames = vec![]; + + for i in 0..superframe_hdr.frames_in_superframe { + let frame_sz = superframe_hdr.frame_sizes[i as usize]; + let frame = self.parse_frame(resource, offset, frame_sz)?; + offset += frame_sz; + frames.push(frame); + } + + Ok(frames) + } +} + +#[cfg(test)] +mod tests { + use crate::bitstream_utils::IvfIterator; + use crate::codec::vp9::parser::BitDepth; + use crate::codec::vp9::parser::ColorSpace; + use crate::codec::vp9::parser::FrameType; + use crate::codec::vp9::parser::InterpolationFilter; + use crate::codec::vp9::parser::Parser; + use crate::codec::vp9::parser::Profile; + use crate::codec::vp9::parser::MAX_SEGMENTS; + use crate::codec::vp9::parser::SEG_LVL_MAX; + + #[test] + fn test_parse_superframe() { + // Demuxed, raw vp9 superframe + const VP9_TEST_SUPERFRAME: &[u8] = include_bytes!("test_data/vp9-superframe.bin"); + + let mut parser = Parser::default(); + let frames = parser.parse_chunk(VP9_TEST_SUPERFRAME).expect("Parsing a superframe failed"); + + assert_eq!(frames.len(), 2); + assert_eq!(frames[0].offset, 0); + assert_eq!(frames[0].size, 1333); + assert_eq!(frames[1].offset, 1333); + assert_eq!(frames[1].size, 214); + } + + #[test] + fn test_parse_test25fps() { + // Muxed as IVF + const TEST_STREAM: &[u8] = include_bytes!("test_data/test-25fps.vp9"); + + let mut parser = Parser::default(); + let ivf_iter = IvfIterator::new(TEST_STREAM); + + for (frame_n, packet) in ivf_iter.enumerate() { + let frames = parser.parse_chunk(packet.as_ref()).expect("Parsing a superframe failed"); + + if frame_n == 0 { + assert_eq!(frames.len(), 1); + let h = &frames[0].header; + + assert!(matches!(h.profile, Profile::Profile0)); + assert!(matches!(h.bit_depth, BitDepth::Depth8)); + + assert!(h.subsampling_x); + assert!(h.subsampling_y); + + assert!(matches!(h.color_space, ColorSpace::Unknown)); + assert!(matches!( + h.color_range, + crate::codec::vp9::parser::ColorRange::StudioSwing + )); + + assert!(!h.show_existing_frame); + assert_eq!(h.frame_to_show_map_idx, 0); + + assert!(matches!(h.frame_type, FrameType::KeyFrame)); + assert!(h.show_frame); + assert!(!h.error_resilient_mode); + + assert_eq!(h.width, 320); + assert_eq!(h.height, 240); + + assert!(!h.render_and_frame_size_different); + + assert_eq!(h.render_width, 320); + assert_eq!(h.render_height, 240); + + assert!(!h.intra_only); + assert_eq!(h.reset_frame_context, 0); + + assert_eq!(h.refresh_frame_flags, 0xff); + assert_eq!(h.ref_frame_idx, [0, 0, 0]); + assert_eq!(h.ref_frame_sign_bias, [0, 0, 0, 0]); + + assert!(!h.allow_high_precision_mv); + assert!(matches!(h.interpolation_filter, InterpolationFilter::EightTap)); + + assert!(h.refresh_frame_context); + assert!(h.frame_parallel_decoding_mode); + assert_eq!(h.frame_context_idx, 0); + + let lf = &h.lf; + assert_eq!(lf.level, 9); + assert_eq!(lf.sharpness, 0); + + assert!(lf.delta_enabled); + assert!(lf.delta_update); + + assert_eq!(lf.update_ref_delta, [true, false, true, true]); + assert_eq!(lf.ref_deltas, [1, 0, -1, -1]); + + assert_eq!(lf.update_mode_delta, [false, false]); + + let q = &h.quant; + + assert_eq!(q.base_q_idx, 65); + assert_eq!(q.delta_q_y_dc, 0); + assert_eq!(q.delta_q_uv_dc, 0); + assert_eq!(q.delta_q_uv_ac, 0); + + let s = &h.seg; + + assert!(!s.enabled); + assert!(!s.update_map); + assert_eq!(s.tree_probs, [0, 0, 0, 0, 0, 0, 0]); + assert_eq!(s.pred_probs, [0, 0, 0]); + assert!(!s.temporal_update); + assert!(!s.update_data); + assert!(!s.abs_or_delta_update); + assert_eq!(s.feature_enabled, [[false; SEG_LVL_MAX]; MAX_SEGMENTS]); + assert_eq!(s.feature_data, [[0; SEG_LVL_MAX]; MAX_SEGMENTS]); + + assert_eq!(h.tile_cols_log2, 0); + assert_eq!(h.tile_rows_log2, 0); + assert_eq!(h.header_size_in_bytes, 120); + + assert!(!h.lossless); + } else if frame_n == 1 { + assert_eq!(frames.len(), 2); + + assert_eq!(frames[0].offset, 0); + assert_eq!(frames[0].size, 2390); + assert_eq!(frames[1].offset, 2390); + assert_eq!(frames[1].size, 108); + + let h = &frames[0].header; + + assert!(matches!(h.profile, Profile::Profile0)); + assert!(matches!(h.bit_depth, BitDepth::Depth8)); + + assert!(h.subsampling_x); + assert!(h.subsampling_y); + + assert!(matches!(h.color_space, ColorSpace::Unknown)); + assert!(matches!( + h.color_range, + crate::codec::vp9::parser::ColorRange::StudioSwing + )); + + assert!(!h.show_existing_frame); + assert_eq!(h.frame_to_show_map_idx, 0); + + assert!(matches!(h.frame_type, FrameType::InterFrame)); + assert!(!h.show_frame); + assert!(!h.error_resilient_mode); + + assert_eq!(h.width, 320); + assert_eq!(h.height, 240); + + assert!(!h.render_and_frame_size_different); + + assert_eq!(h.render_width, 320); + assert_eq!(h.render_height, 240); + + assert!(!h.intra_only); + assert_eq!(h.reset_frame_context, 0); + + assert_eq!(h.refresh_frame_flags, 4); + assert_eq!(h.ref_frame_idx, [0, 1, 2]); + assert_eq!(h.ref_frame_sign_bias, [0, 0, 0, 0]); + + assert!(h.allow_high_precision_mv); + assert!(matches!(h.interpolation_filter, InterpolationFilter::EightTap)); + + assert!(h.refresh_frame_context); + assert!(h.frame_parallel_decoding_mode); + assert_eq!(h.frame_context_idx, 1); + + let lf = &h.lf; + assert_eq!(lf.level, 15); + assert_eq!(lf.sharpness, 0); + + assert!(lf.delta_enabled); + assert!(!lf.delta_update); + + assert_eq!(lf.update_ref_delta, [true, false, true, true]); + assert_eq!(lf.ref_deltas, [1, 0, -1, -1]); + + assert_eq!(lf.update_mode_delta, [false, false]); + + let q = &h.quant; + + assert_eq!(q.base_q_idx, 112); + assert_eq!(q.delta_q_y_dc, 0); + assert_eq!(q.delta_q_uv_dc, 0); + assert_eq!(q.delta_q_uv_ac, 0); + + let s = &h.seg; + + assert!(!s.enabled); + assert!(!s.update_map); + assert_eq!(s.tree_probs, [0, 0, 0, 0, 0, 0, 0]); + assert_eq!(s.pred_probs, [0, 0, 0]); + assert!(!s.temporal_update); + assert!(!s.update_data); + assert!(!s.abs_or_delta_update); + assert_eq!(s.feature_enabled, [[false; SEG_LVL_MAX]; MAX_SEGMENTS]); + assert_eq!(s.feature_data, [[0; SEG_LVL_MAX]; MAX_SEGMENTS]); + + assert_eq!(h.tile_cols_log2, 0); + assert_eq!(h.tile_rows_log2, 0); + assert_eq!(h.header_size_in_bytes, 48); + + assert!(!h.lossless); + + let h = &frames[1].header; + + assert!(matches!(h.profile, Profile::Profile0)); + assert!(matches!(h.bit_depth, BitDepth::Depth8)); + + assert!(h.subsampling_x); + assert!(h.subsampling_y); + + assert!(matches!(h.color_space, ColorSpace::Unknown)); + assert!(matches!( + h.color_range, + crate::codec::vp9::parser::ColorRange::StudioSwing + )); + + assert!(!h.show_existing_frame); + assert_eq!(h.frame_to_show_map_idx, 0); + + assert!(matches!(h.frame_type, FrameType::InterFrame)); + assert!(h.show_frame); + assert!(!h.error_resilient_mode); + + assert_eq!(h.width, 320); + assert_eq!(h.height, 240); + + assert!(!h.render_and_frame_size_different); + + assert_eq!(h.render_width, 320); + assert_eq!(h.render_height, 240); + + assert!(!h.intra_only); + assert_eq!(h.reset_frame_context, 0); + + assert_eq!(h.refresh_frame_flags, 1); + assert_eq!(h.ref_frame_idx, [0, 1, 2]); + assert_eq!(h.ref_frame_sign_bias, [0, 0, 0, 1]); + + assert!(!h.allow_high_precision_mv); + assert!(matches!(h.interpolation_filter, InterpolationFilter::EightTap)); + + assert!(h.refresh_frame_context); + assert!(h.frame_parallel_decoding_mode); + assert_eq!(h.frame_context_idx, 0); + + let lf = &h.lf; + assert_eq!(lf.level, 36); + assert_eq!(lf.sharpness, 0); + + assert!(lf.delta_enabled); + assert!(!lf.delta_update); + + assert_eq!(lf.update_ref_delta, [true, false, true, true]); + assert_eq!(lf.ref_deltas, [1, 0, -1, -1]); + + assert_eq!(lf.update_mode_delta, [false, false]); + + let q = &h.quant; + + assert_eq!(q.base_q_idx, 216); + assert_eq!(q.delta_q_y_dc, 0); + assert_eq!(q.delta_q_uv_dc, 0); + assert_eq!(q.delta_q_uv_ac, 0); + + let s = &h.seg; + + assert!(!s.enabled); + assert!(!s.update_map); + assert_eq!(s.tree_probs, [0, 0, 0, 0, 0, 0, 0]); + assert_eq!(s.pred_probs, [0, 0, 0]); + assert!(!s.temporal_update); + assert!(!s.update_data); + assert!(!s.abs_or_delta_update); + assert_eq!(s.feature_enabled, [[false; SEG_LVL_MAX]; MAX_SEGMENTS]); + assert_eq!(s.feature_data, [[0; SEG_LVL_MAX]; MAX_SEGMENTS]); + + assert_eq!(h.tile_cols_log2, 0); + assert_eq!(h.tile_rows_log2, 0); + assert_eq!(h.header_size_in_bytes, 9); + + assert!(!h.lossless); + } + } + } +} diff --git a/vendor/cros-codecs/src/codec/vp9/test_data/README.md b/vendor/cros-codecs/src/codec/vp9/test_data/README.md new file mode 100644 index 00000000..4302dc38 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp9/test_data/README.md @@ -0,0 +1,41 @@ +# VP9 Test Data + +This document lists the test data used by the VP9 decoder. + +Unless otherwise noted, the CRCs were computed using GStreamer's VA-API decoder in +`gst-plugins-bad`. + +## test-25fps.vp9 + +Same as Chromium's `test-25fps.vp9`. + +## vp90_2_10_show_existing_frame2_vp9 + +Test taken from `libvpx` official test suite. + +## vp90_2_10_show_existing_frame_vp9 + +Test taken from `libvpx` official test suite. + +## resolution_change_500frames_vp9 + +Same as Chromium's `test_resolution_change_500frames_vp9`. + +More information can be gathered from the Chromium documentation: + +``` +Dumped compressed stream of videos on +[http://crosvideo.appspot.com](http://crosvideo.appspot.com) manually +changing resolutions at random. Those contain 144p, 240p, 360p, 480p, 720p, and +1080p frames. Those frame sizes can be found by + +ffprobe -show_frames resolution_change_500frames.vp9 +``` + +## vp9-superframe.bin + +Raw dump of a VP9 superframe. Extracted from GStreamer. Available at + +``` +gst-plugins-bad/tests/check/libs/vp9parser.c +``` diff --git a/vendor/cros-codecs/src/codec/vp9/test_data/gen_crcs.sh b/vendor/cros-codecs/src/codec/vp9/test_data/gen_crcs.sh new file mode 100755 index 00000000..b63df007 --- /dev/null +++ b/vendor/cros-codecs/src/codec/vp9/test_data/gen_crcs.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Generates the CRCs for all .vp9 and .ivf files in the current directory using ffmpeg. + +for f in `ls *.vp9 *.ivf`; do + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc + ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5 +done diff --git a/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf b/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf new file mode 100644 index 0000000000000000000000000000000000000000..e6537f6447944411dd4d3b50a5124da92949eef7 GIT binary patch literal 790703 zcmV(*K;FMZOG!om03ZNXP&qIF0002!0{{R400000000000000KmjD0&000000001j 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