diff --git a/Cargo.lock b/Cargo.lock index 487469a7..9f719168 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826" dependencies = [ "android_log-sys", - "env_filter", + "env_filter 0.1.4", "log", ] @@ -446,7 +446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "annotate-snippets", - "bitflags", + "bitflags 2.13.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -475,6 +475,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.13.0" @@ -556,7 +562,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95" dependencies = [ - "bitflags", + "bitflags 2.13.0", "cairo-sys-rs", "glib", "libc", @@ -885,6 +891,15 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "cros-codecs" +version = "0.0.5" +dependencies = [ + "env_logger", + "log", + "serde_json", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -991,6 +1006,37 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.18", +] + [[package]] name = "der" version = "0.7.10" @@ -1101,6 +1147,29 @@ dependencies = [ "regex", ] +[[package]] +name = "env_filter" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" +dependencies = [ + "anstream", + "anstyle", + "env_filter 2.0.0", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1193,7 +1262,7 @@ version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c4bd5ab1ac61f29c634df1175d350ded29cf74c3c6d4f7030431a5ae3c7d5d" dependencies = [ - "bitflags", + "bitflags 2.13.0", "ffmpeg-sys-next", "libc", ] @@ -1584,7 +1653,7 @@ version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a" dependencies = [ - "bitflags", + "bitflags 2.13.0", "futures-channel", "futures-core", "futures-executor", @@ -2127,6 +2196,42 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jni" version = "0.21.1" @@ -2291,7 +2396,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b8cfa2a7656627b4c92c6b9ef929433acd673d5ab3708cda1b18478ac00df4" dependencies = [ - "bitflags", + "bitflags 2.13.0", "cc", "convert_case", "cookie-factory", @@ -2493,7 +2598,7 @@ dependencies = [ name = "ndk" version = "0.9.0" dependencies = [ - "bitflags", + "bitflags 2.13.0", "jni-sys 0.3.1", "log", "ndk-sys", @@ -2517,7 +2622,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags", + "bitflags 2.13.0", "cfg-if", "cfg_aliases", "libc", @@ -2530,7 +2635,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags", + "bitflags 2.13.0", "cfg-if", "cfg_aliases", "libc", @@ -2848,6 +2953,14 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pf-bitstream" +version = "0.24.0" +dependencies = [ + "cros-codecs", + "tracing", +] + [[package]] name = "pf-capture" version = "0.24.0" @@ -3075,7 +3188,7 @@ version = "0.24.0" dependencies = [ "anyhow", "ashpd", - "bitflags", + "bitflags 2.13.0", "bytemuck", "futures-util", "hex", @@ -3153,7 +3266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9688b89abf11d756499f7c6190711d6dbe5a3acdb30c8fbf001d6596d06a8d44" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.13.0", "libc", "libspa", "libspa-sys", @@ -3207,7 +3320,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags", + "bitflags 2.13.0", "crc32fast", "fdeflate", "flate2", @@ -3251,6 +3364,21 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.5" @@ -3311,7 +3439,7 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags", + "bitflags 2.13.0", "num-traits", "rand 0.9.4", "rand_chacha 0.9.0", @@ -3777,7 +3905,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.13.0", ] [[package]] @@ -3934,7 +4062,7 @@ version = "0.40.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323" dependencies = [ - "bitflags", + "bitflags 2.13.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -3973,7 +4101,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys", @@ -4127,7 +4255,7 @@ version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bd22eb1bbc9137e914022b4994ed35591eea0884e9e3e98e6d9895cad6e1d2" dependencies = [ - "bitflags", + "bitflags 2.13.0", "libc", "sdl3-image-sys", "sdl3-mixer-sys", @@ -4222,7 +4350,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.13.0", "core-foundation", "core-foundation-sys", "libc", @@ -4428,7 +4556,7 @@ version = "0.87.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f7d94f3e7537c71ad4cf132eb26e3be8c8a886ed3649c4525c089041fc312b2" dependencies = [ - "bitflags", + "bitflags 2.13.0", "lazy_static", "skia-bindings", ] @@ -5301,7 +5429,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags", + "bitflags 2.13.0", "rustix", "wayland-backend", "wayland-scanner", @@ -5313,7 +5441,7 @@ version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "bitflags", + "bitflags 2.13.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5325,7 +5453,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags", + "bitflags 2.13.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5338,7 +5466,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags", + "bitflags 2.13.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5690,7 +5818,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a" dependencies = [ - "bitflags", + "bitflags 2.13.0", "widestring", "windows-sys 0.52.0", ] diff --git a/Cargo.toml b/Cargo.toml index 8ee7bd85..003b9b97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,8 @@ members = [ "crates/punktfunk-host", "crates/punktfunk-host/vendor/usbip-sim", "crates/punktfunk-tray", + "crates/pf-bitstream", + "crates/pf-bitstream/vendor/cros-codecs", "crates/pf-client-core", "crates/pf-clipboard", "crates/pf-presenter", diff --git a/crates/pf-bitstream/Cargo.toml b/crates/pf-bitstream/Cargo.toml new file mode 100644 index 00000000..fd565e38 --- /dev/null +++ b/crates/pf-bitstream/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "pf-bitstream" +description = "Client-side bitstream layer for native decode: AU parsing, POC/DPB/reference derivation and per-AU DecodePlans (H.264/HEVC/AV1) on the vendored cros-codecs parsers — the layer libavcodec used to be (design/client-native-decode.md §3.1)" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +[dependencies] +cros-codecs = { path = "vendor/cros-codecs" } +tracing = "0.1" + +[lints] +workspace = true diff --git a/crates/pf-bitstream/src/lib.rs b/crates/pf-bitstream/src/lib.rs new file mode 100644 index 00000000..ed7fa7b6 --- /dev/null +++ b/crates/pf-bitstream/src/lib.rs @@ -0,0 +1,149 @@ +//! The client's bitstream layer for native decode (design/client-native-decode.md §3.1): +//! everything a stateless hardware decoder needs to know about an AU before submission — +//! parsed headers, POC, DPB state, reference lists (including MMCO/LTR, which the hosts' +//! RFI recovery actively uses), recovery-point SEI — derived once here and consumed by +//! every backend (Vulkan `StdVideo*`, DXVA picparams, libva buffers). +//! +//! Parsing primitives come from the vendored cros-codecs parser layer +//! (`vendor/cros-codecs`, see its PROVENANCE.md); this crate owns what upstream keeps in +//! its Linux-only `decoder::stateless` half — the per-AU orchestration — plus the pieces +//! upstream lacks (SEI payload parsing: their parsers classify SEI NALUs but never read +//! them). +//! +//! Scope discipline: punktfunk clients decode punktfunk hosts — zero-reorder, no +//! B-frames, progressive, parameter sets from encoders we control. Implement to spec +//! where cheap; reject-with-log outside that envelope rather than half-decode. +//! +//! Nothing in this crate may touch a GPU API, an OS handle, or the network: CPU-only by +//! construction, so its tests run on every CI leg including macOS. + +// The vendor-pinning smoke tests below assert against byte counts and golden values from +// the vendored snapshot's own test vectors; a cros-codecs re-sync that shifts parser +// behavior must trip HERE, in our tree, not in a decode session. +#[cfg(test)] +mod vendor_smoke { + use std::io::Cursor; + + use cros_codecs::bitstream_utils::IvfIterator; + use cros_codecs::codec::av1::parser::ObuAction; + use cros_codecs::codec::av1::parser::ParsedObu; + use cros_codecs::codec::h264::parser::Nalu as H264Nalu; + use cros_codecs::codec::h264::parser::Parser as H264Parser; + use cros_codecs::codec::h265::parser::Nalu as H265Nalu; + use cros_codecs::codec::h265::parser::Parser as H265Parser; + + const H264_25FPS: &[u8] = + include_bytes!("../vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264"); + const H265_25FPS: &[u8] = + include_bytes!("../vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265"); + const AV1_25FPS: &[u8] = + include_bytes!("../vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1"); + const VP9_25FPS: &[u8] = + include_bytes!("../vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9"); + + #[test] + fn h264_parses_the_vendored_vector_to_its_goldens() { + let mut cursor = Cursor::new(H264_25FPS); + let mut parser = H264Parser::default(); + let (mut nalus, mut sps, mut slices) = (0u32, 0u32, 0u32); + let mut coded = (0u32, 0u32); + while let Ok(nalu) = H264Nalu::next(&mut cursor) { + nalus += 1; + if let Ok(s) = parser.parse_sps(&nalu) { + sps += 1; + coded = ( + (s.pic_width_in_mbs_minus1 as u32 + 1) * 16, + (s.pic_height_in_map_units_minus1 as u32 + 1) * 16, + ); + continue; + } + if parser.parse_pps(&nalu).is_ok() { + continue; + } + if parser.parse_slice_header(nalu).is_ok() { + slices += 1; + } + } + // 759 is upstream's own golden for this stream (chromium h264_parser_unittest lineage). + assert_eq!(nalus, 759); + assert_eq!(sps, 4); + assert_eq!(slices, 500); + assert_eq!(coded, (320, 240)); + } + + #[test] + fn h265_parses_the_vendored_vector() { + let mut cursor = Cursor::new(H265_25FPS); + let mut parser = H265Parser::default(); + let (mut nalus, mut sps, mut slices) = (0u32, 0u32, 0u32); + while let Ok(nalu) = H265Nalu::next(&mut cursor) { + nalus += 1; + if parser.parse_sps(&nalu).is_ok() { + sps += 1; + continue; + } + if parser.parse_pps(&nalu).is_ok() { + continue; + } + if parser.parse_slice_header(nalu).is_ok() { + slices += 1; + } + } + assert_eq!(nalus, 254); + assert_eq!(sps, 1); + assert_eq!(slices, 250); + } + + #[test] + fn av1_walks_obus_and_maintains_ref_slots_across_the_stream() { + let mut parser = cros_codecs::codec::av1::parser::Parser::default(); + let (mut obus, mut frames) = (0u32, 0u32); + for packet in IvfIterator::new(AV1_25FPS) { + let mut consumed = 0; + while let Ok(action) = parser.read_obu(&packet[consumed..]) { + let obu = match action { + ObuAction::Process(obu) => obu, + ObuAction::Drop(n) => { + consumed += n as usize; + continue; + } + }; + consumed += obu.bytes_used; + obus += 1; + // `ref_frame_update` is the parser's ref-slot bookkeeping; without it, + // inter frames fail with "Reference is invalid" — the parser validates + // reference integrity rather than trusting the stream. + match parser.parse_obu(obu).expect("parse_obu") { + ParsedObu::FrameHeader(fh) => { + frames += 1; + parser.ref_frame_update(&fh).expect("ref slot update"); + } + ParsedObu::Frame(f) => { + frames += 1; + parser.ref_frame_update(&f.header).expect("ref slot update"); + } + _ => {} + } + } + } + // 525 is upstream's own golden (cross-checked against GStreamer's OBU walk). + assert_eq!(obus, 525); + assert_eq!(frames, 274); + } + + #[test] + fn vp9_splits_superframes_and_parses_headers() { + let mut parser = cros_codecs::codec::vp9::parser::Parser::default(); + let (mut chunks, mut frames) = (0u32, 0u32); + for packet in IvfIterator::new(VP9_25FPS) { + chunks += 1; + frames += parser + .parse_chunk(packet.as_ref()) + .expect("vp9 chunk") + .len() as u32; + } + assert_eq!(chunks, 250); + // > chunks proves superframe splitting engaged. + assert_eq!(frames, 269); + } +} diff --git a/crates/pf-bitstream/vendor/cros-codecs/Cargo.toml b/crates/pf-bitstream/vendor/cros-codecs/Cargo.toml new file mode 100644 index 00000000..898bfe87 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/Cargo.toml @@ -0,0 +1,17 @@ +# Vendored snapshot — see PROVENANCE.md. Deliberately NOT opted into workspace lints +# or workspace package inheritance: upstream code stays as close to pristine as the +# trim allows, so re-syncing against the AOSP tree stays a diff, not an archaeology dig. +[package] +name = "cros-codecs" +version = "0.0.5" +license = "BSD-3-Clause" +description = "Vendored cros-codecs parser layer (codec module only) for pf-bitstream" +edition = "2021" + +[dependencies] +log = "0.4" + +# Upstream's in-tree unit tests (kept — they are the conformance goldens) want these. +[dev-dependencies] +env_logger = "0.11" +serde_json = "1" diff --git a/crates/pf-bitstream/vendor/cros-codecs/LICENSE b/crates/pf-bitstream/vendor/cros-codecs/LICENSE new file mode 100644 index 00000000..9c1f8394 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/PROVENANCE.md b/crates/pf-bitstream/vendor/cros-codecs/PROVENANCE.md new file mode 100644 index 00000000..659b495f --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/PROVENANCE.md @@ -0,0 +1,45 @@ +# Vendored: cros-codecs (parser layer only) + +- **Upstream:** (the + authoritative AOSP tree). Snapshot taken from the read-only GitHub mirror + , branch `main`, + commit **`5ff6d693ffae0b36935b8fc13092c733b4c2646f`**, fetched 2026-08-05. +- **License:** BSD-3-Clause (`LICENSE`, copied verbatim). Attribution headers retained + in every source file. +- **Why vendored, not a crates.io dependency:** the GitHub repo is a read-only mirror + and the crates.io release lags it; a pinned, reviewed snapshot is the supply-chain + posture punktfunk already uses elsewhere (`clients/android/native/vendor/ndk`, + `punktfunk-host/vendor/usbip-sim`). Decision of record: + punktfunk-planning `design/client-native-decode.md` §8.1. + +## What was taken + +`src/codec/{h264,h265,av1,vp9}` (parsers, DPBs, picture types, NALU/OBU machinery, +their `test_data` vectors — they double as punktfunk's conformance corpus), +`src/bitstream_utils.rs`, `LICENSE`. Upstream designed the `codec` module for exactly +this extraction — its module doc: "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." + +## What was left behind + +- `decoder/`, `encoder/`, `backend/`, `c2_wrapper/`, `video_frame`, `image_processing`, + `utils` — the Linux-only halves (libva/v4l2/gbm/nix). punktfunk's `pf-bitstream` + + `pf-vkdecode` occupy that layer. +- `codec/vp8` — VP9 has no dependency on it (verified) and no punktfunk host will ever + emit VP8. + +## Deviations from pristine upstream + +1. `src/lib.rs` — rewritten: keeps only the module decls and `Resolution` / + `ResolutionRoundMode` (the sole root items `codec` references), both copied verbatim; + adds crate-level `#![allow(clippy::all, mismatched_lifetime_syntaxes)]` — vendored + code is not held to the workspace lint bar (CI's `-D warnings` legs would fail on + upstream style otherwise). +2. `src/codec.rs` — one line removed (`pub mod vp8;`). +3. `Cargo.toml` — rewritten: `log` is the only dependency the vendored subset needs, + plus `env_logger`/`serde_json` dev-dependencies for upstream's in-tree tests. +4. `cargo fmt` normalization under the workspace's rustfmt config (mechanical only). + +Re-sync procedure: fetch the AOSP tree, re-apply this trim, diff `codec/` + +`bitstream_utils.rs` (expect near-zero conflicts), update the commit pin above. diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/bitstream_utils.rs b/crates/pf-bitstream/vendor/cros-codecs/src/bitstream_utils.rs new file mode 100644 index 00000000..07d59dfe --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/bitstream_utils.rs @@ -0,0 +1,788 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec.rs new file mode 100644 index 00000000..23876f40 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec.rs @@ -0,0 +1,17 @@ +// 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 vp9; diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1.rs new file mode 100644 index 00000000..477cd906 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/helpers.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/helpers.rs new file mode 100644 index 00000000..f8df7cd8 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/helpers.rs @@ -0,0 +1,186 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/parser.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/parser.rs new file mode 100644 index 00000000..ecb40a9f --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/parser.rs @@ -0,0 +1,4294 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/reader.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/reader.rs new file mode 100644 index 00000000..7daccfab --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/reader.rs @@ -0,0 +1,251 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/synthesizer.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/synthesizer.rs new file mode 100644 index 00000000..4faf7346 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/synthesizer.rs @@ -0,0 +1,1825 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1 new file mode 100644 index 00000000..1bdc6b4c Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/av1-annexb.ivf.av1.crc new file mode 100644 index 00000000..e69de29b diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/gen_crcs.sh b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/gen_crcs.sh new file mode 100755 index 00000000..007b63e6 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf new file mode 100644 index 00000000..83b53b27 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf.json b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.av1.ivf.json new file mode 100644 index 00000000..195ed7c8 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1 new file mode 100644 index 00000000..83b53b27 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.crc new file mode 100644 index 00000000..43013f62 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1.md5 new file mode 100644 index 00000000..ad04cf61 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/writer.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/writer.rs new file mode 100644 index 00000000..fcc8fa4d --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/writer.rs @@ -0,0 +1,203 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264.rs new file mode 100644 index 00000000..fc13025e --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/dpb.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/dpb.rs new file mode 100644 index 00000000..658c600b --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/dpb.rs @@ -0,0 +1,1302 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/nalu.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/nalu.rs new file mode 100644 index 00000000..364c47ad --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/nalu.rs @@ -0,0 +1,124 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/nalu_writer.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/nalu_writer.rs new file mode 100644 index 00000000..4a6d4f86 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/nalu_writer.rs @@ -0,0 +1,311 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/parser.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/parser.rs new file mode 100644 index 00000000..5cc52b53 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/parser.rs @@ -0,0 +1,3062 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/picture.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/picture.rs new file mode 100644 index 00000000..a2c488c2 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/picture.rs @@ -0,0 +1,439 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/synthesizer.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/synthesizer.rs new file mode 100644 index 00000000..f983bb9e --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/synthesizer.rs @@ -0,0 +1,593 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264 new file mode 100644 index 00000000..359c3732 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.crc b/crates/pf-bitstream/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/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P-high.h264.md5 b/crates/pf-bitstream/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/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264 new file mode 100644 index 00000000..31328608 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.crc new file mode 100644 index 00000000..b6ffab18 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.crc @@ -0,0 +1,3 @@ +ee936370 +0e5e577c +bfe430af diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.md5 new file mode 100644 index 00000000..9e0796db --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P-B-P.h264.md5 @@ -0,0 +1,3 @@ +fe6701d54768bc37c76e630435e8fe02 +3298d47365ac1f9dc0942ece9e104246 +7cb9209603f53ba995f7eee20b2bbf4b diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264 new file mode 100644 index 00000000..c1fc49ce Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.crc new file mode 100644 index 00000000..e8b11af9 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.crc @@ -0,0 +1,2 @@ +9fc67012 +7f0b441e diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.md5 new file mode 100644 index 00000000..a741ae55 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I-P.h264.md5 @@ -0,0 +1,2 @@ +115a8e6899b71c04e32a0254ba62b30a +1fa3f5a930e08943e6d6bfbd5d43004e diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264 new file mode 100644 index 00000000..afb61ac3 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.crc new file mode 100644 index 00000000..83389050 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.crc @@ -0,0 +1 @@ +7dd66ef1 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.md5 new file mode 100644 index 00000000..180e18a5 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/64x64-I.h264.md5 @@ -0,0 +1 @@ +d2304abbf0349ec63324741bf723960d diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/README.md b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/README.md new file mode 100644 index 00000000..62f33e69 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/gen_crcs.sh b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/gen_crcs.sh new file mode 100755 index 00000000..c0eb7b21 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-0.bin b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-0.bin new file mode 100644 index 00000000..08b46e22 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-0.bin differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-2.bin b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-2.bin new file mode 100644 index 00000000..65f72dec Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-2.bin differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-h264-slice-data-4.bin b/crates/pf-bitstream/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/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264 new file mode 100644 index 00000000..76990939 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.crc new file mode 100644 index 00000000..6ba15054 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.crc @@ -0,0 +1,250 @@ +8e0a948b +0d25f469 +612a0a5c +0a452520 +6c83e97b +d1598a6e +26892325 +e7eb5f61 +e527446a +f85a9d52 +16bfd3c5 +e837620a +38484895 +050d070f +b30cfcd9 +bec9e023 +9b272332 +f6a542ba +bf978015 +1767b90a +f3755595 +5ae99ff9 +5b8b86a6 +44cd3b51 +d99e1fab +17761096 +271806c4 +b0d17f38 +01f033d7 +6ae775c1 +42933e3b +11f61f13 +4ecab452 +8e50b509 +dfde6c00 +06703f5c +92a02866 +c6f3a13b +4371bed0 +5dd9e344 +cfd668a3 +abfc6c45 +ffbc45ac +be1cbdf9 +a6dd2c68 +fc838af2 +d8f88c05 +e377a83d +8acea967 +cfcae361 +47ec0343 +c5af87e3 +a4c1d94e +080ca745 +74f48838 +00480284 +f2da9c1d +8bb000aa +32857438 +e7fcb9ae +9bbb834f +3889cf50 +a3ec330c +6963e8b3 +8de3e2f3 +8e1d1dc1 +242fd47d +e11cc789 +9e558667 +2ff16820 +d75fad55 +5f4a5907 +83182bc7 +450343a5 +dad31a6d +bb365f64 +cdd2d57b +99c0e687 +52eecf8a +951cd566 +f29f0fef +d1165eae +0df626f8 +6fbdfc15 +7f5180af +c1f6a321 +7d9b9418 +a0f25570 +c5af5562 +acc18caa +0d3d93a1 +cce8f670 +7804b84a +ed7999c0 +38dbf871 +09143151 +866c7c00 +c782f291 +65605fc3 +8f37e317 +aaf653bf +2894b605 +7eeba8f6 +cad6e5a6 +bce25e79 +cffbc84e +c29dc1b4 +99ac23e8 +7805efed +78623121 +88767543 +a5e2df6f +555355b0 +4e646cd0 +1fa3064f +dc4f65f4 +77c52338 +41287dca +0ea69260 +2fb243a8 +3de20c11 +ad7fd2c0 +79f73884 +cf89a598 +301e0a62 +3e26fed7 +8e8dacbe +628cfc2b +5c7fe45f +06892492 +fb7d8b50 +ddf98de9 +5fb4c485 +501ccf38 +e5f40baf +07ab7574 +8596934d +f7066878 +27e166e6 +10a37320 +39dc9664 +ca9e1642 +5d23f171 +dafd915b +ddcbc72c +56b4b83d +3bbde005 +41ed4060 +68caa834 +957b84b9 +5f6bbbfc +98e64dae +ebd05871 +c851d8e9 +f73fbbe4 +7451ac0b +cbfd7a78 +901fe589 +9ed86bd9 +1e9fef87 +faca6981 +98b7bd7a +e1d4bdf1 +b6a95dee +3955fb57 +90d28016 +64472746 +5f2b76df +478197b2 +c59fda8c +169c2bfe +74210735 +94662d44 +f16e4ef6 +f2134ee3 +f881db32 +0d927db1 +ab77556a +12a65d29 +632965b4 +807d10f7 +339e5f7e +be2c9336 +bc593f8b +c9dfe52d +fc738a40 +17699e28 +e8be4231 +b4264279 +895a00b1 +41eb9726 +e804873a +c6a4b014 +bdc91323 +14b6934d +dd31a422 +0d379528 +4e4b45cd +9f6773f8 +326f3f46 +6180b23d +61329916 +01a1ecf3 +8b77abcf +4508213e +dfc43ab1 +0937305a +a9d22bba +541a1ba4 +5eb3490a +4702020a +190299ca +202ef749 +911daf20 +b7b63628 +3bbb965c +25971699 +3cf16f5f +1e95278a +273193c1 +48794404 +a5a25d98 +7c85b782 +d17a5be3 +c218d70a +8ce5be9b +eed76688 +7fdd906e +136a9e51 +87674a67 +0c1f44d8 +6e09307d +f4f2d7f0 +855c52e4 +e63633cc +8baecd74 +08371d8b +18949a47 +c253b241 +c94aa045 +e272c69c +f2b73158 +6b6d3a84 +76e9bf4d +4c05b45e +1a774816 +47e007d1 +37e715a7 +8d8bd4f6 +733862ca diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.md5 new file mode 100644 index 00000000..6e92fb47 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps-interlaced.h264.md5 @@ -0,0 +1,250 @@ +b318f483b819b982a9756e53d7e15648 +f0257b3a3e42c43334b5114f7af458c2 +7e53dcfd409361c9747f2e15498bffec +d149b038ca22572e33a1f395e5ecee08 +eb27dd6ebe6c63da3383569c0775a831 +2cbbf7c8d7519b6f5e8ac14363b4bfa5 +ca1a19e2110b2129f11e8b49127bdd85 +286936def7ad4df160b360d65ac9b46e +b33f9984878150925a7967072e99d49b +075757bf9b1875431fff1fef1f8d757b +68d3706a9b387f97e6ddbfd4abc57ad0 +bdde980b77c72c36b4f090c9b5da5344 +2574458dd66165304420f6ea813d63e5 +35fcaf6785eb36fc903eb5279b6c29aa +ec4f4e19d7e54e26d3dfcc30e46f8a57 +bffdfe52242097b034c1716a4f3c2c28 +5fb0282d92050c9a5c7b3c12dd5ced0f +6167bc48f2e77dad220a3a6aa67b05bc +d3cffb696153090eb014e6a88802bd4e +9d175a5550156b1e8bca6099b4c09f61 +7fbd2aaeedd6f3d6cb9603910c7cf2c2 +56e21b1c18fff15a62e9933e442fe8ff +546312d22a42fc9b67b08e18d6b82852 +2509014b171245b4a3b692527c2781f3 +fb31a14a29920145563197157f60b0cf +64985fa4c405b139aa60202819bee68c +7886fd15be015e09715906507fd7ee6b +f86709eedd0ea8413c30e5417dab459a +0d3b8df30ef8188a889dc7cdeb02b21f +255db4b53038918896afe70d26f7a52c +c35945881b96b536573f7eb5a35d9bd3 +809a7dcc7966d196b61e0645025e959b +fe2130868d0026602d29492cfb77365b +1f341d6cb526c6cb2b18a4919d13dd3f +14a9658c25c52651b75b009cf505c268 +f1b76a3abadb11253729430c7f333fa8 +cd204999e6f7912ba9caa4f625314a7d +3701e818d60637dfc56407d88b79feda +819907f586f4c7515106b4083ddbad47 +c43789f2fcd506cd7ac5116c9ef4d15d +19ae171488b106301ee54078b6f5b5df +21ca7fe9e1ab4f13ef5a7f8a041d471d +6d23d3b81f1c4c787e0ee9f62f5a9849 +3c9ea82d2bd157595b69138c327133a6 +506a237ea2ec916734d9511fbe79be10 +5001323d2e1b5410def30db2f59e4261 +f4e315814b0812511fbe50d5c061e324 +a687ca61e888fe0e36db694a870a62f5 +9f6f17e40f587c27ba7a24227f279285 +1e09cb2dcafb86be51648f2a4e14dc24 +db5fff370bd822000ca204fd64bac350 +0ad486dd80931116e0eedc3c2938a201 +c2a0ee00978debffcbd633efe9299d97 +21fe48e7b1666fedfeb8a6e7b67ed7e9 +bd2f4438022e4007207af7db7e488674 +f4c6654ae5325c06d7248b423d2dfff0 +f24ccbfb2bb7856c3a6a03570b01d1df +253e90c855cb01f47c98b86c02ab5038 +9b1dc8a79e1d93d8756ed0cfcbfe5137 +aea93cf9423bc3dfc384faaca064397c +fb030c4f06693f3610802851ceb11232 +9307c5df0bef220e2dac16807f7f237e +1934c1a1a72b0646d0e9e71c15dc4987 +5ddc040a414fc5a141ff949e4b127d4a +a1d5f1d432006062b5d6b9443c717ad2 +0589d0ebf3c8d2364237e80d41a2b475 +ddcebf0cefb4f874243424fac2e3b6de +cdf1dfcfc39fa58d1f9092bf38ba7e7f +167ce118cb7d087ea441c56fe4d35fb1 +20dc2343a99d4f973832173523c2f5b1 +3c54348b7b75234d86ee3dcf298b00dc +187f13356155b3966596693b3ba7f6fc +e64ec5b7d6554e8976f9d55620ae8411 +8e4d7d9d357d5146b824655e99284939 +221da170942678c1bb96f106bdacca65 +10397b2182e00d3c2f3521adb1125b1e +820a2cbffd802eb8444d7d321903855d +fb6543d46d8733d2fe8505f058b14e4b +088bb42d269720bc1d023537f7b57213 +5d34e835cf4fe44f26e6ea7df3699323 +7525e57a778a4b298ab8a1f529dea231 +f6611d41cae1236372a61794515f3286 +fa807ac173b70892aa9fbbfe5cf097bf +b30a12a5c07b96c5d2a7ef716a40e214 +b699c6d2b59396338c025cf72e28235e +567c19149bb2089eae35fb58e6d3ca9f +d3d47ec2f02bc7d564192a98fad66e34 +bd8293548cf345333c892503bc02762e +84c3e877420d17c1536836f1b4b0cf42 +55c15bb8eb4cd81545314e0ab6535c73 +e47f3fd5c096033b0d1d3e9de8e05dfe +63e69377f86e999221ce62dd101fb530 +8ca70fbe70b840ffc159f2ba5890b472 +e5da1d908351429ba3875de6e541a294 +0cd2b4b3d9dc052d4f3d82235a469809 +8c47d7a5e67175912811cf24ce1be9df +b3a7c2128d325519e6e788ffa19b867b +bf803115b52d1452e2f46355deba01a4 +5db69b7973fd9e6da6aaf7836219d495 +4466c1bf9d8cb075799e7259b06c3dfd +0df28c89649a7ada81ed6b73995dacd3 +60adb4f96fdf22479e7d1edc583d532f +af2802fee9230f40f7662f6eb6318518 +f1a5beadc8a5ba88b74d31e211b3da2b +407d4028cf27cfe3cb265d932dd01ad8 +837ef722923ce9df10ead5332e2ad1b4 +d468d3075b95985c692d1182cf45c1f0 +cf750f31df41c4fd0f64797d7e776243 +bd0881cfb36014d96dbbd8d1872b449a +b3fdd5a0235590e0606eb8223425c206 +c2130fcb9907554280a2f1024bf5b58e +0b4cc296d9d7354718341a0b241d5417 +09be4cf122c20e92a8460571af96236d +2c8763ee3dffb8a2ab123a0b98909531 +bc07e96aa9fd80daee6b6747f3eb10d9 +5191f574d1aed73c2fea26ba696ecd73 +cd863713a5588c5df97f1272bbbc8537 +1ed61be740e1c0dbca9e803537e913fe +6ff899d76a0599bf67b4898078d10c8d +ee1f3a668c150ed6dfae060e97dc46cc +d73c55aa581d36c4f7f534f1e03c9418 +d9689cd24ae9b08375530f7ec41a574a +08846be9ad2afa27ed851634d9cab4e5 +1668585e3d72b2bc736fd5b582b17ee8 +5d2d6df2d6c40044086e8faf5e20fa76 +53e50b205ac2de14ac79d2a658ef4327 +e73d59cfc81ec50e7aa5ca9087aaeae4 +2ca1fe1fc342c4c3dcb28089e82604a3 +b1cce1a87a2486705b59517c895e817f +86143d602317e1302c8d1825bfb201d2 +7c962811ce295e6ab9c4650cae19319e +be87fbaa40ac8989649700e9a82d0ce9 +d3fd645e47efd43950972463ffd74e3d +6ed763a90aca846393467bd37bf6ecc2 +f888abbf25d47985669b2ecbbda04063 +1c64db945e018359d80762d0f1e82f85 +a5c8ff3c974ebbb722e9d6ef92de4c3e +7611866a4cb7d346752bc4700621779e +075ddd7b4bf5bcc6d308065b94c9e4e6 +0a59ab9ec9d8228b1b3d554bcbdafa26 +af6014ace2c40bada1bac0da4ca1855b +86955b7f88078d167b381366229f8a42 +f8c66e921bf190453f6c64ba9dbeb3d5 +c353624e039f8f5a6ea83196d7559886 +bb418dee4a5aab8c9c1e5f4d32a4bc8a +b56240f383319fc8ceddf6864f715a5b +544ab0aebf0e6b5bac10f5c27329ef52 +dde30afce82bbb95752b39662d79648e +ffe0f8f122b0b575c17a745d98af418a +ba476f1704cc103f303dc88d6aa5fd31 +ddfc7326b1e137d4338da33585c94fc4 +32a313e1553677d00ac43cb8610186bc +4fed1c0a1959a9dca55c1d126c704ce7 +cc073c0fcb57c2a0ac7785d3e36289cb +7e91a82a927a14d0be7a4ccdce702222 +85a1e2124c2acb27c2eae947e371332d +b8dad5132989583791ea3180ede734e3 +b749f5cfa51035c2c901c8f51fdae26f +63c8d810da89f6d93895744cf6bc6092 +c4c32e4c185cf04da9e6f6cb4b228c52 +6d48d28f00b6cbd78b4ddfa056c4b24f +a56ec3b1d282706257e9539d97708745 +3487a2b855eef272eca5c04ee2b826dd +5d35907998c38c521a0bb08dd114900b +f01a320fef7ed3cfa74fd5e7b7214491 +3655861d44a93225eab5a6f6aa4dbd9f +adc67f4388f9e4aa42232f19db02690c +096cebe6b60bc924def53b2d9413febb +8200dc7f73ae39a7fbeaf6b7fdf11e2e +ccc47ba3d01aaeb9859cd998d3472efe +727e467f3789d4b3805318dd600e8d41 +ab74f71926d8e1b5d0fb89904c4f0e56 +2ef13831a4100e211309658d5c02a177 +796c34d7ccc6790fccabc9be3e42db9c +b1fb2e39582d47ab8172702288b2fcbc +ac5d037d7457af598c9b7fca0eadb384 +7a82d0ec070abcbb2d1ef16bc8af47e7 +ed68a5b6911d53f073b778294192e44f +55b14258fc94575ccea51bc4bcf6cb61 +2502f7cd3fd06ad7994acd5ac8995508 +afbfe5035e3cdede39a1250b1d91eff4 +c7bd0e18288966e3fc902e401a1cf92a +a3243a3f3ed1f32bf4e2d5ee06cf69ab +3538beb96dff67a1d7b81a317950d038 +ff24aa585a23d93671add56e45ffdca3 +912bcec63bdabb5a4b472df4b4f557e1 +d59ad48f9b4378ce89b4bfffec9b916e +ea4e4951dc51b56e07bf01c0d4171549 +7197584855f20ff4903c247939967dca +9f7482cc0d499ab79f2eb5480807ec6e +eaadda1f5ff577d617e6c4c479b5f563 +a4c9e70aebeaf8b4f986fbf8bd8aa902 +10f3080e8374ba0397f1e296fbb60350 +b021caa467751d617842f66bb4e62b47 +444ce58ff856af0ee2ba0e99ae37950b +b61d88b82f582f32723ee5dfdb1d34de +55f921778031bd848ebddb7dc0d63db7 +bb9ad6dd7d13d4e412bdfec31783992d +a4d4f8ce07d3496017024897233b09dc +ffd49b1bb1ebb2afa89ac73305762656 +69d843e05f072e3616e9fbeefe1feb9a +4758c49357c312d6187a3cc7322d40a9 +0200eae7e77a5f7eb0ab6a52b5934c43 +6f6de279c5b1bf22064d6173e0f4c659 +dd1f5fc9e67d0f2375e72bfaefa72590 +bffd8a460d7ee4746ced0abd2e108562 +2d638249a3d755b2c778f6b384b862e8 +1e5322f8df0406fdbd2c99d5eeed0a68 +41e1a633a6adfaa70198f5872272013c +9900e6ccab80f965f0f42f5f466e51f6 +0da72ec72eb2eae9f410267cee90d1b7 +c808dd41b542e64db7ee2b6770eb99b3 +2d8b514f3e2257521b2ab872ea7d4ea4 +be6a7befa9d91157e6a70bee2cdb9516 +ccc15622babeb66a984b81d80a713966 +caa22e1262b249b7e0dbac4bdd59e391 +d759cf8e65cb2b805a7101b32bfe64d2 +43da07daa63a0ad6c39e5d3b468b7df9 +2cf45923c9b52de4b23a34ae07616bfa +1c5dd7b336a9cfcfa7772e4df2f62075 +8261e8791fc1d8a5097af6ed2c1487b8 +e97b262b8f2056851ba19b6b1b23a378 +567442abf4ee97d491572bd868d50daa +cf25196079d433cdd802e87d4211ad3e +e7295385f1842ede9e7e5e48602fac00 +6e44cb843be327f938e1558fd22d9aee +e8efa873da70fb0c55e95ccb3b98dd0c +1f520e477d527dfee845187c8bba3433 +da789f8b71e7618b6b614e7f38c6eb06 +4e9f640a8f317b0da5d3f24e87708866 +922134326b2d0223cbd3a679adc908e4 +272aa55d0137df113bee04f8c06e4d46 +6e5f5d3f6d2c88fdc80f26c3af4baf39 +2fec6a2b1154841837b77a456627de14 +71c562a6801157465fd5df54b87504b9 +eec76ebe71275d86b69a1ca42a8da947 +42e10d7a5b99affc5d9ed41006e701f2 +ae0aa8ce889e3ff30b7840b7a2b74737 +06bebb86bff1e08d071adf6284bbe3b1 +0fbe06a9de9f8195b03364748b6455e3 +46f2d55d237687a1b9d3b326c56da6c3 +3ffbbb180f1c3bf8303967a0840297af +19cd3d6d0641fe34da63c78cfc94cb72 +953d1514d529804060f71d1430fdcf7e +961279642b7ca4f0ddc329c2c0d7a819 +cc4ffd1fe4dab49b3a5ad06948fd3875 +32f0f8ff923031ac246925020c11868c +ab56616053efde4cc28e379d689d380c +253c3fa01be26ad95522f95bb3927132 +0b137261e168b892d7b6b2255198d545 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264 new file mode 100644 index 00000000..b1c36d9d Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.crc new file mode 100644 index 00000000..71110e4f --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.json b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.json new file mode 100644 index 00000000..14e8ef24 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264.md5 new file mode 100644 index 00000000..fc0ca72a --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265.rs new file mode 100644 index 00000000..44f3f8a8 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/dpb.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/dpb.rs new file mode 100644 index 00000000..63f906d4 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/dpb.rs @@ -0,0 +1,297 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/parser.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/parser.rs new file mode 100644 index 00000000..44a4405d --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/parser.rs @@ -0,0 +1,4848 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/picture.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/picture.rs new file mode 100644 index 00000000..9b9d01dd --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/picture.rs @@ -0,0 +1,158 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265 new file mode 100644 index 00000000..f3ca72ba Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.crc b/crates/pf-bitstream/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/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265.md5 b/crates/pf-bitstream/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/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265 new file mode 100644 index 00000000..50935e39 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.crc new file mode 100644 index 00000000..47096b06 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.crc @@ -0,0 +1,2 @@ +2407c115 +396fe8d4 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.md5 new file mode 100644 index 00000000..5011175a --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P.h265.md5 @@ -0,0 +1,2 @@ +afcabbf0be007e76b1f496e199eb07fa +cebc9437bfc8501523c432d3fee3621d diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265 new file mode 100644 index 00000000..d7be2a40 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.crc new file mode 100644 index 00000000..319743d2 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.crc @@ -0,0 +1 @@ +a5a83a48 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.md5 new file mode 100644 index 00000000..e1c0e019 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I.h265.md5 @@ -0,0 +1 @@ +15caee73c93560200b9e240c6f5d7d0f diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/README.md b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/README.md new file mode 100644 index 00000000..c9cfeaaf --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/README.md @@ -0,0 +1,17 @@ +# H.265 Test Data + +This document lists the test data used by the H.265 parser. + +## bear.hevc + +Same as Chromium's `bbb.hevc`. + +## bear.hevc + +Same as Chromium's `bear.hevc`. + +## test-25fps.hevc + +Same as Chromium's `test-25fps.hevc`. + +The slice data for the first two slices in this stream was extracted manually from GStreamer using GDB. diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265 new file mode 100644 index 00000000..f82236f3 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.crc new file mode 100644 index 00000000..dc790819 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.crc @@ -0,0 +1,60 @@ +97255112 +c317718d +bd113d45 +186209fe +75726ee3 +e4a90dbd +1da8aec0 +f537bcee +bdcead02 +edd12fd6 +9814391e +ea813192 +8e3f606c +7298a718 +c25ed8f9 +54585aa5 +c5d6811c +83ffc178 +ad9b6189 +20e3b289 +ea852c44 +38af63d3 +222e116b +cbf5b144 +5cc11d3f +561aba19 +c1b0021e +97465654 +3f69533a +95724dac +4fd1865b +00fc6666 +262e9ecc +753e21c5 +73820a63 +6023ad07 +6baf0154 +6b658956 +c7f3d4d0 +b7fe7729 +652e5667 +76b64fad +98f934ef +64a737be +e561fe00 +2bd0799f +53d86edb +07cad162 +c4857692 +9ab13929 +c445eb2a +f0e890de +1db971fc +d03cc3fa +69d2cdba +172562d2 +356c55d0 +515bfbd6 +5d8dfbc2 +ec0874fb diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.md5 new file mode 100644 index 00000000..c572a900 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bbb.h265.md5 @@ -0,0 +1,60 @@ +e4519fce4b5a50a8ae38d2b42864e550 +c7f6707ea7b2aaa3a9c027e00bf2064b +21c016eb7a57804d8637b03bc7910b31 +f744d8b67d4836629beac1ca4efdad9b +8e593571ad75b9b19c3b14b44033bc03 +3f76c8397f7829d948ddb50c7050b4bb +d91e343dbdb64dfd15f09abc4c460b06 +62d5c1de7a7ff8b4cdfb51cb33746bcd +01a4c4d5f770d3cdc5429264f6a1a742 +760de93c5a25439a89378fbe50c26f3d +d56f52f9252745321c723121824e5d21 +be626892c70d6fd412d7bff504852529 +ae49266f3b90dd1238f7ad83ae86886e +7a9fe15547cbcb51557bfd0b8370e0ee +a5b81f4e52fe54f67d109caa200486ec +34395343b832af13dbe37ebe745506e0 +36d6e24c8a000b4f8d5c1aabba85c185 +9be3b6e5b0b77c136b5abe8a22bded59 +5122057efd33ed5634b755a7b0581382 +abaf35e215001a6abe0dbc63f5b4ace1 +62ba3a52072f80993c24cf9a3abda06f +21b225675443d0142e104ec88f7e9569 +b561e6555b65ba6cebf0d73eefe4f319 +ab2b1a568bed6f9bafe79cf15dfd85b5 +e8c3645830f80e082e8b02e3cc599cc2 +82d655fb233fea3fc3377b4d3dfc11ff +4a7c2a51149c1939cffc8cfc87fdb953 +32295652a5ff998c90fa18fbfc066fdc +ce8974e2519a2babc43fb29106459cd2 +589fa92d80565556776c2108f676c439 +2bc1878534bf66caa6d686d0dad5a936 +b3d81309bb62ed349bed2c0fb17a5593 +c941ae94ab205aa1c27b9b06df4b57de +e782fa5f5a7302b8246c29880e9c2f9d +eeabb3a6c5fa1ea0f17a9fb911f5b612 +26f27eafea985de929a48287162fc641 +da4f4da7a16a4326c0c458f3dba12ef6 +7697150fa654713af6a6ecc07ad7d3dc +67696b8c27169cbdbc18d093f794b328 +96ba8f5d17e7b35e78f5ac78a816a364 +1ab2c0d535d26c72d730e101600f42af +f8b9d8fc231ccd64c551c829ef25089a +eb004174e79dc8dbb533922639a725e2 +fd519318eb8c47e5fac6fc767246bfed +471dbd17c2a374ab7dcaf64c345c1884 +fc28a8f484573893f6f501a4ade8ac37 +1e2dd38a14d5c13ef76e89d142f73951 +0fd6a338f4ecb3df053d105bc95e6ee1 +a42fd1e8bb069ab5c501fbfd655fc1b0 +f252d07215ddd6687fb3019f2710c939 +c8df7b9cbc16a26d782494b8cd312be4 +966a2f3fcb066179feef80e68f13e212 +b99b1100b6950144e70fa47310451340 +9b3502c7dc1a4ca2579a29f6b17af84d +9a81be9720c64ce3b343fd20bc17bb14 +e5a67048a9c1ae74b8a97c5a27d6d2b2 +1527a719633c75b67ae6c36ebe550812 +86b1329ec84332fec1a75330e7d7a174 +6fa5b5faf93d70e96ffa215e4628ad9a +ed6279f7b90727abc8a193745170a318 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bear.h265 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bear.h265 new file mode 100644 index 00000000..f2a0545c Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bear.h265 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.crc new file mode 100644 index 00000000..a3c5b67f --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/bear.h265.md5 new file mode 100644 index 00000000..4163d712 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/gen_crcs.sh b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/gen_crcs.sh new file mode 100755 index 00000000..4c7db423 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-0.bin b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-0.bin new file mode 100644 index 00000000..b06651aa Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-0.bin differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-1.bin b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-1.bin new file mode 100644 index 00000000..03c6a152 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps-h265-slice-data-1.bin differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265 new file mode 100644 index 00000000..34bf55a3 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.crc new file mode 100644 index 00000000..dbad0a6a --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.json b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.json new file mode 100644 index 00000000..145262a0 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265.md5 new file mode 100644 index 00000000..b460f218 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9.rs new file mode 100644 index 00000000..d26271ec --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/lookups.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/lookups.rs new file mode 100644 index 00000000..4a189de4 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/parser.rs b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/parser.rs new file mode 100644 index 00000000..3e91009e --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/parser.rs @@ -0,0 +1,1520 @@ +// 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/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/README.md b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/README.md new file mode 100644 index 00000000..4302dc38 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/gen_crcs.sh b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/gen_crcs.sh new file mode 100755 index 00000000..b63df007 --- /dev/null +++ b/crates/pf-bitstream/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/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf new file mode 100644 index 00000000..e6537f64 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.crc new file mode 100644 index 00000000..0ced9e17 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.crc @@ -0,0 +1,500 @@ +30434565 +3f479427 +1a3815b4 +003efaf8 +a326e30b +5e48c696 +4a95cdff +d094e6b9 +f88fb600 +3166c8fe +0631ba57 +0f994c4b +fcfd01d4 +0d6085ca +83f02696 +9a6281b1 +e477c455 +83fb3e76 +0dab98b1 +2704ae4a +4c735dae +665987f7 +fd3a4848 +89ab7ede +99abfab3 +c602e1de +22b58401 +3cf8420e +5a33eecf +847e256e +acdcec41 +ae5c98ac +cb7b8f57 +5f58e096 +8f5cf9eb +c2bb56a9 +72708cd9 +d60f26e0 +ac56d0e5 +56fae066 +c8f77144 +957ea038 +75a28a96 +602ba7b8 +8dfa67dc +145d0c16 +07d84b15 +d5ff745b +89b9c4fd +476ddf3b +73e3833f +eb946288 +39916706 +a62f2ffa +b62fce6f +cec06605 +d139ea59 +5250ceb5 +b75ccc92 +c79f5e51 +8bf78f86 +2f55a022 +0b4d2c33 +ee21ac93 +e5880dfb +98ebd835 +d2bd50b7 +e63409b9 +c7f17438 +b4671d62 +6e012ab2 +1d71249b +1cc7e30c +8ad0ebf3 +b3bf9734 +8f45ae14 +185495d4 +edcd3b82 +b7b7bc38 +e62517e2 +3e5767dd +a0b59888 +1517dc9b +213303bb +1c0e1d69 +48849645 +c41fe238 +3423ddb4 +e8e9bcb9 +aca22302 +85142fc3 +034b1fa8 +4fecedcb +6317d541 +b098f009 +7d13c5cf +b6b6127a +1ee289ad +3a3f70bc +a6b8e785 +185c7dea +08025f9e +a6647fdc +1916ab72 +d2906114 +2c833567 +86374614 +7dc4ad6c +aa9e4b12 +9a7e5485 +4e89084c +b0e7e971 +1c7d3e11 +6f1f48ef +d2eb406d +4952e5ce +8ad0dee8 +5581a685 +f430f28c +18adef19 +f81c45a5 +66ecef00 +6b8023cf +58d96699 +5db8ddfc +2f8d6d4c +150426dc +9d14824a +69879063 +465d1e5a +f4d03906 +3bbb4545 +74e922a6 +be20217a +dca8cce3 +7d1e9b5b +edcc5564 +5d6611d8 +0698b853 +a62b3ecb +b34c5938 +e26c121e +39cc64a8 +cc34cef7 +81cf2401 +d575fff6 +1a7f0bdc +f0c6d006 +5b106cfe +9f2888e0 +6f2a63ce +c1155ec4 +12da69bf +c1dbed84 +671c4163 +5dad13b0 +d2017cee +496bb413 +30434565 +3f479427 +1a3815b4 +003efaf8 +a326e30b +5e48c696 +4a95cdff +d094e6b9 +f88fb600 +3166c8fe +0631ba57 +0f994c4b +fcfd01d4 +0d6085ca +83f02696 +9a6281b1 +e477c455 +83fb3e76 +0dab98b1 +2704ae4a +4c735dae +665987f7 +fd3a4848 +89ab7ede +99abfab3 +c602e1de +22b58401 +3cf8420e +5a33eecf +847e256e +acdcec41 +ae5c98ac +cb7b8f57 +5f58e096 +8f5cf9eb +c2bb56a9 +72708cd9 +d60f26e0 +ac56d0e5 +56fae066 +c8f77144 +957ea038 +75a28a96 +602ba7b8 +8dfa67dc +145d0c16 +07d84b15 +d5ff745b +89b9c4fd +476ddf3b +7aebabfc +5098261a +6be66915 +2c0dac16 +46e4498a +4bb011a9 +1db45608 +51d4b563 +0c70020d +5fe5a5d3 +9fd0b920 +db24b6fd +2900f825 +ae09ecfa +7c220368 +58cb372f +b4da90fe +c62f659c +b6b990bf +665a5990 +aad5e55c +aff234d3 +c6850001 +366cb3ab +b95a04db +f34882e5 +93d19d35 +37834afb +998b8b4b +ab26528d +4f39e702 +5bd3b3fd +ecb5a096 +d434b006 +d44a715e +37f1c963 +367b4f79 +154ef078 +8a909d4e +7cd512da +2bb88121 +e2bfd733 +dba85108 +eb49fd76 +e2c9f19f +25a38992 +742619f3 +e9a279b4 +90c4de29 +4ff923d4 +e4b56000 +1a6b4200 +839fedd8 +87650ead +0345d74c +691fa48c +cedb2e03 +0b3e2577 +5f5b60b5 +95b19c13 +633d49e3 +2da01d07 +a114b659 +8080d857 +f52eab8e +b5348da2 +12453318 +a6589d2b +189c908d +eb83c448 +4e09feba +506361d6 +72de5e28 +49644af2 +ec508bb2 +efcd9e22 +947abf26 +47d491a9 +f3819b93 +19bf5692 +a1a6ff76 +ed33d700 +c4def3ad +ef10c624 +fdabd9d9 +6ad10a8e +d719e93a +593c7aae +1e3ccd7a +bf625826 +3f174d5c +222db35e +0884b2e8 +f3b5be46 +07794925 +21c9233f +35041427 +10d883a8 +765d6db0 +54dfccc9 +4801da6f +06c7e866 +69208a52 +3381d557 +c3f91b72 +1dece653 +0828a727 +6b808625 +4db6f222 +64982c8e +5782aad6 +dc1ab775 +51ab86b2 +c9f02103 +4ebe8270 +85d90d4f +62194ea7 +9c4d98db +15e04acf +955f3ec1 +6a9205c9 +6a915ccc +013d43c2 +4a7e4926 +bce87d78 +8db1db9b +f0e7eba1 +cec75219 +1cb9aa20 +37163dab +b619ad9a +c3348021 +ac44a4df +5ef80c45 +cf3238b6 +db148947 +cfebcfeb +68c2ba8d +97bd3333 +38b9a39e +88239b6e +21dada0c +3a3240ee +309c773d +782b8c8c +a18fa947 +d96adf14 +d130b2bf +13ca5914 +5d0252f8 +94482817 +f859fd3b +454ca588 +bacf1f63 +9685ff99 +12bfbea9 +758d0396 +d1dc7f1a +f332a5dd +95ed5526 +320373cf +66ae44d0 +d7f60521 +174e8778 +d8f1cb9a +505edb44 +e218879d +8ee171c8 +7f0d1f7d +00a0f4a2 +b77fcb6e +9d9e118b +3ef2a6ba +bc3ec3cb +ab1f4821 +dcb9c237 +aedf52c5 +5191f352 +e58687bd +74584d91 +42310f91 +c1276911 +c81f69ec +ffe3479f +0dcd9a22 +00c1d7cf +6308e6dc +0a7a9866 +032125a4 +68f91bf4 +e488c252 +a7534dcf +24d336a9 +98a8bbb9 +caad1bf4 +64f2157e +52a304ac +f15eb12c +c6dbedca +da11a1f8 +be321a1f +ae891efa +0ec7ace0 +ce45b0d5 +77137631 +5a0b7286 +19498745 +e1ba1f6a +9f76aa6c +c409dd1b +92536c65 +bb9ef77a +2240c34a +4b698459 +52bf0ddf +2b357e41 +b821dd12 +9c428cc4 +f58cb68b +3f1adb2c +ebbfd9c5 +f47f62d8 +7305e1be +79ef7a93 +a83ac98c +ee9fdddc +b2a237fb +22dab010 +fc03b4c2 +90cd6a71 +7cfac9af +50472042 +8d46b423 +34ca61f2 +01cc9d54 +b191f0a7 +64330c48 +25c84ef5 +e3d608b4 +1faf320a +dc42008e +a4478667 +54cc09fd +cbf1e2b5 +82c03181 +76a575cb +c88fe2a9 +92f328a2 +d792a66b +ae89fbd0 +742429d3 +1045723d +084976fc +ea999295 +2c79adc2 +ca4fb5c6 +0ae9e41d +e3d52ad3 +d453b8fc +eb24dbb1 +1220c85f +5b631b96 +eaab2c2a +c10c82b3 +01cf2040 +381ac249 +431bc361 +57df9be3 +2010ccd8 +6e5d770b +7934dea1 +37a6d295 +69b537b1 +1c5b6117 +5aef90c9 +22c21587 +f45a95f0 +46ff5fdd +a2c00527 +bf2c3a89 +ca3def6f +40e4600e +12255f84 +6439fa9e +89f0b37d +13f60bfb +6ede3385 +e2eb309f +c1dd01a5 +cecc9d99 +d1845fd9 +dd87d2b0 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.md5 new file mode 100644 index 00000000..5159a03c --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/resolution_change_500frames-vp9.ivf.md5 @@ -0,0 +1,2 @@ +d5a38844026bf51029f97b815fb04b19 +401c6bf17f34a1185d5381009e58ba65 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9 new file mode 100644 index 00000000..385d1b4a Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9 differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.crc new file mode 100644 index 00000000..83a05e0c --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.crc @@ -0,0 +1,250 @@ +1abf0870 +32ab9360 +fd2a8755 +795c3488 +05605011 +c836c6f6 +9811bd68 +2ea553c0 +2cc69486 +9784cc5d +dc95c93c +9abb41bc +fbc1180f +a4f5e378 +0ba301f7 +49af5c91 +1a33dc74 +c3841cc4 +3d22678d +9ba82c05 +acd3f53e +5e147078 +3710613c +55c0edc8 +c4a734bc +ee06bb4f +57ffbd97 +4e3b52e4 +9848ca34 +a5f6a263 +7bf13fc1 +d7cf6fcb +a49569c9 +5b9b87a3 +e1732f37 +fec42824 +1e8514df +7580ba76 +e07ae78f +065a11ed +e254d9f5 +67ca9179 +b74ad987 +8f0151b8 +c9d5220f +34788052 +c8269a62 +98108c54 +43db9efa +1d6e8a25 +790b24cf +93badd6b +43276faa +ba4561aa +78d7e0cd +aa11db2f +98c23282 +0d27f401 +4450596e +a511b3fd +45aaf3f4 +8b6afa2e +798ddc36 +c16d2441 +90096480 +32d5f72e +ba19403e +e2d0ad1b +f5306855 +184309d4 +f1134993 +49a1ae44 +e724a782 +76ecef33 +7b48a1c8 +80e678a4 +14217558 +21d6bf24 +907ca9e4 +71bdf4b9 +be9b2301 +bf953977 +a5c17a0a +b7c27ba3 +dd13bf91 +600d03eb +fd2d8874 +a2d8af0e +4c21a3db +48353f6e +e3e6b60c +ef3b9c28 +1a4df0bd +af18c549 +4e2da67b +77a51623 +86b999df +f5b45a41 +b0d5b913 +46b5ed49 +f64a266d +ff157d94 +67b1e678 +705a8536 +0a7f2695 +02c328d4 +ddbee2a6 +259fe83b +9164f5e4 +f7b740e2 +07026fdc +8887118e +ee734407 +7544e6a0 +d1cd9b46 +ed639972 +1c791217 +52ca157e +b3ac782e +3592f784 +ec3a0a53 +ba29434f +0c9dafcb +4659b0c9 +c645af06 +5b9c4202 +5a89bde5 +c7e3498e +c202031f +51b8e7d9 +bec84a33 +163fe7a8 +4e05e423 +d1e3f398 +dce754c5 +795ccbdb +5d1e3e29 +737f110a +20d97337 +f112e2db +cf7148ee +c305f875 +f18e07aa +bfdf8135 +6a216daf +8fbe0aaf +639343a0 +0e2152b1 +695c3d1f +22065e7b +98acf61d +7a3fcb35 +dcf38665 +b7a69707 +03066f3d +1ed4cf65 +193be090 +6f3ada5e +018ce957 +0eddb953 +9348cf1f +6e6d4172 +28ec3fe9 +529bfd00 +084d2315 +f3556816 +cab6043d +0262486d +7d3c4498 +61a96b90 +1cfe545a +e92f9e8a +57d3eca4 +45051fcc +c73e5a15 +6bf04540 +3e5cbfe6 +e9315867 +c59ffd57 +a7eae6b6 +ee526a3e +978aebb2 +adcf6a3c +979a7dc1 +fc3f7b6e +2e716a71 +878e3d42 +3144b8d5 +84224d07 +408a0c1d +af9a7070 +c86e81e7 +7e04a9fa +b190255a +5ce924bd +8799f259 +29e5e7da +be77bd1c +8e001635 +75f47834 +fdde4db9 +264b2528 +5586fe04 +c2a06cc1 +4a3fb4a2 +45f4f1fc +0fee683a +987dea92 +83b24331 +b4acfdcc +03679bf5 +8f9ae6f4 +5cef71dd +947f45b6 +2aac1e37 +028426d1 +2dad1dde +14a9c696 +c59d9bac +54de7644 +094a7b74 +ae7e70be +43d26c10 +6232d8c9 +cc65bf7d +59ee8a5a +807addde +e7d82793 +7fe92022 +4283f764 +d9a91c0c +a77f54b3 +02aea8de +b44fb9ce +1a116375 +46304da0 +247c2899 +a9d9cb2f +88bcfc60 +b2ba9844 +333d1188 +4c2f12ac +a4d54033 +be8e652e +1a37abc4 +2ca65b02 +95d7a371 +55840fec +ed610cf4 +c0809111 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.json b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.json new file mode 100644 index 00000000..6a11099b --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.json @@ -0,0 +1,260 @@ +{ + "profile": "VP9PROFILE_PROFILE0", + "width": 320, + "height": 240, + "frame_rate": 25, + "num_frames": 250, + "num_fragments": 250, + "md5_checksums": [ + "a6ddd21f5f4e7424b6e7a1f2925fb33b", + "41c77adcfd29abfaad62a057855adeaa", + "afdb44531614034e4a4a90c805a5d3b1", + "1fb247507d6e076feefb7281846d4938", + "597957a1bb001769a675d5be58db3271", + "c023f0ad8c3051e536ac9c9bb1d5eab2", + "64fd982393c290d092e1cda39cd429c7", + "915e863eaba1a7957f554b5cce006e1e", + "ace4ad6c26023dbf12f4c5e897e996f8", + "166ba862f99f7e0145f0b3a8a46c53a4", + "bf401e2e9084c80cb3c22d2d05dcc627", + "94dfc278df94cc355b6019096133befd", + "c88a98cb020b5cb9372b528ea876e3e3", + "7c05949db143606204b3ede34fbc0472", + "ebb8798f465fbe8083bd0255a41e17da", + "59e6ad68fb6e7c78383426c2dcb768df", + "57f2f71ab176f78b815182c107649b56", + "0713b3db71e138b41e1c64d5f76292e2", + "913558ea3033285c2ef414c29cd9e36d", + "f66bcbf90aad3f2973d7210ba221c5ac", + "02371beb715f84b4cc366ac78c9597ed", + "332355fc07a4eadc59ff5b5f0514347a", + "5bc08aa98cb182e58bab48967c26c938", + "cf1a6f1bc177d45bbbd06a21417973ef", + "c69f55a8cce9b1af971f96989c2ed50e", + "51b067bd3faf4b624e265dbf3bba6e43", + "9323f02a491fa931645c5add54109890", + "2fe699e08540e1a211e259909248f91e", + "e4bb4daddbdbc8c9c1b7398adfd2664c", + "9356a0ac6701f2231c6a12d6d3eccfed", + "597e71f32915badf51be8ffc255aab74", + "08a0dcd28bcaa495e95d15d2645d9c03", + "48742951820e0b22038c17f715f55027", + "df8b6480a30359d33f0fc5ff5f22cbb2", + "25d9f7a3dfc3bd6f7e581af20daa749f", + "fcb79ce3d922d1478e34399f29fd69a4", + "6de189116760547566fa6e3885a9b2d5", + "29da43f8a80e83a54db3c83016578bf5", + "b327993a3a9782630e8f38830b99f1eb", + "6a25c1b232dff00e78565f2886bfa728", + "fad26a4f6912ca0527c5cea767eca873", + "9d05717db2bdb502179161b9be1d4604", + "47f91eb36e06b5c7fff2c538f3026c39", + "a5611dd903d184a7ad3b3c5a02027694", + "9b977c83aafc80e375655d81406fb3d1", + "6eb2923e12b36b91b1d38a50c0a477d7", + "14acfd2a71f163186da78c84b34ce0b9", + "9f5395258aaff37e8c87b6c07df978b5", + "d5a22ec4e0ef752d3877afa7ca1db26c", + "005415db7bb57768a6ba92d4a43afab1", + "40cb57177361e07b523c95a206f20be8", + "e645c544fe2c8426bbe9ff1b24c6be8b", + "94a976505f27a90649f553dc7a193fe9", + "3b435d0e9e6af6ecf3676cb7299ef6d3", + "c169aebf19b7c6f3717ee0e097036507", + "c970f90040212ca7a392ca5cb7c8708e", + "243932bc414d8de7fb3c1bdfb4fcfa91", + "3ff138cd7f150eebf63a2f16a0ddc3f7", + "03ad4bcd723e5f2a3e96de33effac6cb", + "5416a2155944c3c23167d5a00e8e12b2", + "0620389d338d9fea5c6867f261f35452", + "377b5550cccafcd5e3142da159791add", + "68b1733c716380a04d0dbf21fc1f30d4", + "c9b872ed955bb13dc0819661f2b96692", + "fcc79ce029edff15bb7c2c5130d421a7", + "933967daa7eb201accf440799113547e", + "b64df022a685c95ca39c6c860e1f5ab9", + "8cb58810932a51ab29b10c691dcd50f8", + "5dfd36a6f1d476780724792b7a33b45b", + "25580f9904ff6397ddbf57dd93623aaa", + "77d61f46a7421182f43385fb7de3ad28", + "1e7adc2c2f99538d5c629cf906e82a65", + "60336a4c5b6dffcac7f3d42cd8c8d8cf", + "7ed47daa771ef93fc2eb05cf123184ed", + "bc03e5d85743ecb2b9f408c4814ccc03", + "5096b7c2eee1d8d7bf7fc825db35fc6b", + "38c7db4fb6532a9c827a6d2c0ca15640", + "93188336537ca3b189075b83afcb4304", + "4b0a2f9d16710b28e1b5b4f2a6757101", + "c14cfb8c07018c8926d849d0d6910d1c", + "d0d67b916206b75134f4b254d41a5747", + "2390a0cfa71bced2b9fb3637cff30921", + "3ab325d11b4014ef6f70734127fd4d31", + "9964596397e69118de09fb1e44fe01ef", + "89522f41b2b45984a1a27d54c59b41c0", + "b3270c89e1278984d133f4c2b7fc0a70", + "fdd6bb9f2b4b89584294221de8291107", + "79e5d6ec50f8d136e8a01bd0f150416a", + "1066212964911081dd41a8c184716589", + "5aff5ab98073ca06b2cd02b044ed2bac", + "52fa9f744f083400fa1013a9a296783a", + "ed586b59b27f1f3147d0c33cf94618e9", + "ebe31226ad166d53db606dfd46f65c4e", + "9208a53c77d7a69273cbbd386bdaf38b", + "5cbc9121a9decf62cdf538fc3b6ac6ff", + "de33f01b71d0b84bd6641864e5e03c1d", + "68459b19450133bebfe87e8658840d84", + "f93988c89aff87855e8a46a388231ea0", + "02a92e92e273e30d11c65a9c17afbdc0", + "194ee974b89f3c7ed85efc0b3067bcf8", + "111c441b0ce29b52370d79a7c040a319", + "c5c668728b0a0def951aabb873747c64", + "9fc4ce859ad1060df8583f11650e5e69", + "ac7ea5ac33d992834e18d5a80f7865bb", + "ede582c26d225a3cb903504a2817685c", + "4d491df588bdebd988d8d89e1f3aeae0", + "3d24041ab9920d06219f02b762d1ea92", + "5bd75a66e942a73ae09bb24936335869", + "5d335e05b9b578f104306a3577acbfdf", + "866816a72e249b6cf0f9f6e33b65ac78", + "afaaa9d734c02fedb83ebf6524e9bab6", + "d7d1b2774ef4ddcb191fc9fe6bf28c29", + "482908be24d0988e8e6b40df91c1bb1c", + "1752814ee8e6d3097cca487d503edb0a", + "128c52fa88a9e3df2b928519812fd3f2", + "cb41c09b32c25ad921c87538b62f70f6", + "a1be95f5d67cf17a1540557605398b5e", + "28fcc49f81a46bfb2fa1e39b7ddbef2c", + "1e6fe0400d792d432c03a6fbb7a346da", + "fd357048fef2312acd9bd1a84f08dd0d", + "2f4925cb7f740454ca31ecad6da072fa", + "721c71f2297e2cde48d23e2d14209b70", + "eb39d3512e79c54299a2f2360d001523", + "d3b3c4661b95a8102a091dadb3f61a8f", + "abed47e70a4fcea8b5905e27bc4d91d2", + "33144b402f5b60a0d7109727d678367f", + "347710490553e286bb41635a87d51440", + "e5d90bfd73c660c136f18708cf691902", + "2ccb92441cacb6fdc449092df990f7c1", + "cd457fae93314e50d5c2b7f1f3cec91a", + "d00e8d32b7b8e03211ec61f8329944c0", + "7481f50fca5fb8758a12932b40eea3f8", + "24cda7a1abbbd3f90286e529ad0ee446", + "fe49c757004f028382165c8ea24208e7", + "d903372b98ebe31896a591a1a6cabe0e", + "28f937e819685a1b5e73cd404e998fb0", + "5bd6d6f0037891cad42864acc85b9824", + "c5c6a4e219aebda78e3e9d2c91a48564", + "0a367f3982856c5bd984c2f866b255db", + "8d62ebc501823bce65b840cfedf2e75d", + "49b0c80de766ff81ab183cd611d1d118", + "da984ebe368315c820f2a1caaafd2534", + "710b15893183f93f2497c2e98ba56e0b", + "3ec5a99fcf6f43eedff8dd9995aa4704", + "67b9d9e89fb300ae3ebb03b396fee273", + "6194ca3c338f64f144326486d118e793", + "e85a130d9149590919e72a2f8ecc5f1b", + "62fbf66d14bc819353086e442c581616", + "bbc6b58d27c1623bc87dd81793dd72a9", + "5804683f0f89bdb8e43508e93ed2d17a", + "72fb2bd71150381878acc658ea547020", + "351e4493573d81f4b8cdbf4a94fa2dd5", + "573f6ebdef825cc73a6aefa219518add", + "f671a8002c56f70623b1d36f6d1ccce9", + "bc8f0deb204095ec70effde543c4f087", + "a0e79d0c3e9c3734c90471e17cfaa402", + "1e85a120057917174101240f66fbe12c", + "bde29019b9d44035c78ae593d882c0df", + "f1036a0a0190b198558aeae5c8539100", + "7d50b367eeac4a7ea9d0f797f3f286b8", + "133a4ed3fb42fe986117df1fe34f07d9", + "1e899dad271bb94f8d768c2025d527d3", + "258bcce7005861b2a6c2e547c81d66a3", + "30723accdb0788aea00ff42610c0dd99", + "a60ab6066b2b8258194cb10d8e10c206", + "321b86641c6e978e98b36ac367982397", + "be2b37e04fde984631e06ee04c8749f8", + "af1de1b1e1b4104aa99b563527828c68", + "c10f648540156e7379ed764ad424e233", + "716f1d5d894f0caeb6e060c365b1c68e", + "4da20c2960132d909767cb328a2c70d7", + "a5d0eb676eabcb9b808c1b7c6b312c3e", + "c06cabd771e2c42a9e2c3ca7738525a0", + "e87e67d2e0ffbd9762a4e99ddeba53be", + "3eb39b20537cd8cd12f15ccd6848f672", + "f3212f83868cbf07b06774c0283b25f2", + "746d5d5355f10892237e9d0dab554485", + "1956d40b46bf714d8bec22d784c54611", + "fba00831cfc6f6d948917c0e02a22ca0", + "c744b4f54702e34f47c0f4c4f40ea615", + "7c7adfc5fa5d03b5b7e07cd6e8293b4a", + "5825b5ba1960c3b1a88aa1e10e5f0474", + "d90b42024fda5312a0d886cdc2ce20f8", + "0592eee5f90d9dbd2ac17b03ccc0ed2a", + "2ff9315ce6dd9f4c6ec501c481dceba8", + "0a3ed552d91de9a403120f2e118cfad0", + "ee32ab17ec770aba340cb68e181275d0", + "3a36fa6fb7140d2354ba1ff2f1d287ec", + "77e8b7f41eb1cd46c389c9c3fecfed44", + "7f8f710b7bbd5a033300d0e4fb47f71d", + "28eefbb77d26f698bb658ed7f58cb17c", + "811d2afc4e6f5e8efcfc03563d1374d2", + "53d51c878f1aa62fb2753d67f2decc02", + "1a20e2c1da568ac283173c60807d7bb7", + "f77541cb2afd1633cb4e5294e99de8a2", + "e859d77a2cc87972cc7df32e6c625f00", + "18d8d2ab6cf205ce70316ce7a3e7e3b8", + "fa2e35da2ad12625ed5f50be46bdd61c", + "463169e7371dc37f3d082a5f166b904e", + "5e7f7593bf77b346cbf9906741623d94", + "60089de0fd61bf8547cff25da75d79db", + "609dff1ad553656a98bd6ea178f093c1", + "34ba858360b9943770e3b2c9594dbbdd", + "02dbd49e186b241f42c59f9f77d74e13", + "0632f6c62ee2b72e3dcd3ebbaf3f8e59", + "0cf0fe04e5bda8159176da2150ae56dc", + "ea48f3c5ee4fda7b7c44ad0f9aed7ce5", + "25a1a468110f7819dfa688bf08bb08c8", + "702f29070dae81d71fd4b6ec16766967", + "bb8e273c432a96b4c8fa92fde5210a54", + "702cccf5fe0f912c4a369e0c1b0d1d6c", + "bbdef9ec58db645eef1b52129e914dcb", + "3816969dc45df4e2d39db44f460bc0cc", + "db25e3bb20d5a30bf9170897e3075732", + "43875d9f264821d179f0d861cd5c82e6", + "467945c3124b2e4a4d65ce6a55e4f889", + "47ad1273bfc4b8575515498fb3e68570", + "8cf9200d7ce629344fad9e7772c5f099", + "a2d784b07f4f64ba41309138f408bf3b", + "aa7de9e0f6765a7b5a9dd6a37de1e474", + "4e82c66108b942323c95223d729731b0", + "4d7dc2ac3395345977678913400d671f", + "817546d45e1d74c5f5211002947ce94a", + "d4b41f15f106231c194db91bba1c9350", + "a61f9dd98999e8b90093817f280046f7", + "3a6943d566df3f8d9aa342554f2a30f6", + "1738e3cb6680ce1b09c9dd25b7b51d16", + "846b40c6d1023862b5ab86c558cc75e6", + "8a6156e1d440d1694e9df3e7c0b0a2dc", + "f6be779252c9d6d9409c94a899f23090", + "11086af958413f0ae757571535d7ecc8", + "701254468010c99da0d1f98a39099bed", + "2624e584871b8fed98168aaa31c7d264", + "8fec6cc28ab6d0072a9ded2861e13f96", + "700b06457ff7f99b47e5412c2fec7324", + "b34f8ff874d0c9e4730ad94cde30722e", + "814a5d8724ac31d3af0d1076c4d3c2e1", + "342befc9829aa640b36de1085e3849c7", + "e7879857be414f0215a5716dafcce729", + "874ea752b65d583b44615f23ec00e3de", + "8b555f7686aec7c61df83504275b0491", + "7a1763fab40e8e049c8320e603be4bab", + "e50ef5703efbd0b38266057a05b4e56f", + "014bb33e138fb10157a062cd4906a032", + "94224a6b01c4b088d429a78421b777d7", + "8bb86793ac81fc54f3a6c9ff354ff9f4", + "6b90162e6ba9308ac891770528a0b2a1", + "5a63e607041536fa3f633c769f0a9e17", + "3b33f8e78890e8a41d8cd93ebee7078e", + "fbb59915d51c7f56386777e70ba2dddc" + ] +} diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.md5 new file mode 100644 index 00000000..a68323ae --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9.md5 @@ -0,0 +1,250 @@ +c147a6c80d9209e5a1e992d4e610a95e +807f9c05d06ccfb7f6a77018f52c07d0 +df198689de44a69924d8cfe791e5dc84 +856efa94272c5b0fabec7e1179563806 +b5586069cc2a58a38d0271136949c760 +5a0a2be53ff9bc19f131469bee430262 +40374e2eabce3cb67b9f0d9992f31edc +2c0881752530fa3fec18f3aa3277f938 +c86341015022064f0070cc8b70f2edbf +ad05d275a93b63de6327436b48cf9e80 +4ed78beec93f00af0d6cf78cac200494 +9273549b482b591a9da3543c490d4847 +59b84f1b3e971e27212a8f690d8920c0 +07b78dec9c8070b40e87fdaa18b4627f +ee7e9db173349fe2ac2c199831bd5eed +ccaabbed17e58a6f77da8a5b00f9f24b +436aada1f8eeef8b11bfb454310461a3 +2bb8c17d108b4b5b9594461efe48ffe8 +04e89161b5ddd1cee2850f19c1ea9af7 +bf527e3dca6c046d5b0aba1cdcd95f28 +564fce13d1ddbaa1d587bae33130e0f1 +e7f84fd6b22935634144d85818ed0ce0 +389109c513f164483e1cdf67d1d17e20 +888e96338562ea5ac11882ff897bc45e +0734d839755f4b660e282fbdfd303da4 +86ba362ca374dea486493877bc83830b +1549542e46f6c9a412779461b3b2e1c1 +b90ec322166c72e41e9605f7a0c00e34 +2e293965ced430e72dc44daac571ae96 +8952e34cad20b1ad4460b0510c255caa +d6cfbb47f87f2c6d27bdec96fd86bc80 +57fa0881469337f201b01d025c255070 +e3c562edb81b8ed8bdda8ead100b47f7 +60faf70b449db2ce46010a71d10e9a37 +2d5fd1fa5fd07021be4be51e279496d7 +8094a24162877f21e8e2c876631d911c +cc675882c3a3998f19875e291c1f158a +4a049974b54cdf78f45c775bbe21cfc5 +6f78137fee79f8882b8bee2b5830aebc +25e75b7a102c32988bb676743d46da7f +bae608bf6d8b156669cc884ab37e6d7d +883651afc0bdaebea1a7906f51b1f07a +71047b3c17d1aeb3eb3cdad867a6cc1c +fa5b4d415cca2d417e1ee5fd2177eb6f +fe4dccf6135bb46db063b1b1b09a1b1b +4f111590afad4f98c70ef2f7ae2ab302 +c39c4467809fdf8b396f2f4643c72a39 +5eb42d6e49b71cc75748052104cdaee1 +07398ff06268387fd10a956b12dff28e +5e568ba441a984e117d5a9362267e4f1 +6bcbfebe110cc1014a325b0be3ebac52 +3ad42d2755b6e9fdbd2d92abd212d636 +a2a368e017d5cb835d0953a00a6bf2cd +c3955a9d37d474036e1457d486093aab +584fcc9af0395b6e6c3f28d64473bcc2 +511a81a66dfd9c83b15c561a81fa45d6 +62364a344fd1f9706c011a197c6b0cf2 +3f1c26938b57f0973fb73c064dfa9bf3 +dab959b019d40ca4114893d774945fa8 +1c33008422a5351bc61423912fabc669 +f20ec6445ed4d41421fb6f3fde5bbaca +ca43b98fd0e7c94c60b403f2acc2e948 +a5efd2ae15724d229993842a9c919011 +da547900d3e9eb01bf4765e2354fe88a +7259dc31f78637c9a70ef43906fa5807 +be346c4bb4516f67883802c200179e06 +8c8f6991b9124c0aa7dbdfad68bd2c9c +6049aa8416cf2a3b7df3cddd86b44e64 +363eaac4e65bad5556a3e175d643123a +b6822e13f6db695c822afd73e0b989ef +61c375d0f9dfedc67304e920c08301f1 +7a46f29451ca727ef7361420cffeabdb +80edef5ace96379c40db611885141c34 +1697174088d86b17d6b372c20aecadea +148fca7be383caf8adf8433a25e4d4cf +c2eac8e9ca853bea1e76acbcd774e2a9 +c8310d2b03328f17f9ea602df338d279 +fe641d1b7be37c948e30bde65f609fff +00c13ad8d2766ab091d96960cf6609e1 +44d4d30bdcffdab69e16d4f07aa66c9c +62fbf74f8cdca21c0c7814ef3c515a4c +9762ed61bb1c78c1dc84bd67d05c8016 +31a4ac35433525329bafd764405058ce +6bf0c3b23613d7fbcb04e8b715eb96e6 +a20bde62736096ad152544f195afc41b +0b1f0a21f64623b268ee55f8ecb0dc27 +8d77996095e9a258d3505bbf17fda0b4 +49eb262a315de652dadb7a22c1e8d601 +d466dd053e522f653be20c2d81540c03 +6e1afad04dd7f9f72a59c5a173686ab8 +9726e527b395492dcb6ff72a0d5a4796 +f272035189417682ab283889a9dc1fac +2bbd94a07488d59f8147388f0c394a40 +f7d750c1a7a88d1684f42cb535430280 +1ba751a04d93c7bcdfce9391001997e3 +68cd83e38eb422ced19838e7167765c4 +f003f95a69519492fbf6b08969a0c261 +5701ccb9205417896a193a800d32c38f +b712418e2276267553b1056782a7e96f +1be965ee1da3c8dc4e1e5bd01f850ef5 +02da1c68b982cd7a30e104320d04b36b +61e0a8d7df2d01909aa463701a3bab30 +dcad9c4c4bb4855c2fc31c47f5a0c12c +c0de7fba39fcee42577dde05f4d8b0af +6052cb7ece90c1c64861fff7421a9d69 +ef669757e94008168aff1646b5c04c0c +e03f821f36ca1b59c2054a5aa72cb82e +a32fee7e3e39f3e69f69c940a9782493 +dfbe0be3fce78c0c6bea90058b5321c3 +669b2200b96bbe5049a37ef88f86b1a2 +d95206a7ecad0f0d154d59b8b81c398f +f1939cbbc7f9803b03551e0ee8e976e4 +54a25909ecfc627c161ac9bd5dde0dd1 +be6a415aec10a459061cab5f856c1dae +398d56c536ac7b051fb8a96d7c12e147 +06b0b34464aacfb4221d4a1d17781a70 +d25f64611b9d7bebb6124c2f0c39250c +94c2b637e08ee1ee8c79eb662a199450 +7febb4a4f4b711ea70e86e7054b41277 +04b884c1af9ac9806495c65de8dcb748 +00dd0c5d65933dfa721a61e3683c54a1 +ab6974a3b1aa3a510e84ffa04b4b3f89 +297d608c848d13c71c42289326d169f2 +89c514f843b17cc07ec1c4c08feabee9 +8055e52cacff9cddfe49432d20d0bfd7 +ccefb98fa05dc5863b6deb905ff1c3ac +11406e832f5a98f031c717a30d4f3861 +f173396fc7e7bffd8d6df55551dcf51a +54d7421b70b4d182e3fb4dee07c70178 +2e43573aace01f9fdb6a6dd6537407f1 +eb6c3ca7906821794693205a27609dcd +fd686d26cde34b7d11527a9794813e3f +9b82ec0d251d5e294ccae92896b0bbb2 +509ffb563baa8b528e463dd0c1606229 +6344839db52a21e64cf93fb23a1cf8a9 +a0781ac759a9017b75a8e6ee58261adc +00cdccc8b20608e9fadba7cab2a52aa4 +d2f5a572735fb09ff0a2765366980985 +9ae66ea9466618e8d30f3dfaaf05cdc4 +8e18ba56741b48ce88c23baac7b60d83 +1530c8f53fe1edd5e290eb70969c4e0d +9fd6cf8b4992dd5a83617fa0540fdb49 +eeddde09d799b59f9676957095f7b545 +9acb5a05373f57a9dca7ba745bea066f +d85400d97728f0850a53de94eaaefa1f +88ea3f31c331b394a01f6f4ea44dbe73 +38ca4b29acc8922e012e274b2306b484 +4d3ac7cc0f7df2363eeafc1d3916904b +e7307e525f8a833de862cd1b953757be +ee6208f9f33ed8680e2bc04babad0843 +2e45bfc6975708fd3e7e0c4524755231 +3dddbaf8b60bfc79492749d5230ec23d +713eba2ea4aebf9d73c525971d6e9bd4 +396a002c7217fefd9c760f63ecfd0e44 +1ba18897ba27c7913dc78eca0b465a6c +9f80d56ce61776d19688b71fa20e66b6 +fe0fd66f26f13ab470e7696ed57440ab +7ac35e1a6f9de5a5247c55d9971ed02c +b45218ddbeaf8b1c1cc61836d49bf331 +647c6c98430ad0f6304d1f7ab54b0791 +a5b2c31a77130f10c81f9b1e8c0b36db +f85a9d6ab3fa171217d01c9eddd520fe +d294eb44a6972cd33c6c9ccd492ee4d9 +c776f43b7b879779221a2cb6fe363062 +dfbe59edcc434a5a2efe95aca98929fd +c5b7bc6f751ae1da4c4d9b596c208300 +aee6993c994fe114192cd440f2529ba2 +c16a35af9d237d8bb00bfd78d7910177 +5b17eb3d2946acc9936f54b092236e18 +35ab79392d55fe4383171693cc4f1b07 +65bb08bf3f1fd317baf8249bc39fccce +6e9db766a1563d160a646d3cee97e82e +dd4c49639c8c3d7bfb950efeec7af608 +32ba1749595850aa1f7e5430c168f1a9 +9aefbafbc6e058822f743c48cbeea6e6 +e73cbbb909c9a6e6e016b454313e00b1 +b5cdf19a48d33b8476b5b751a2f0398a +6ab09966690b0ea5c87269d4a0547597 +43ee16fe7b45c986fa99441b8c0a64d2 +1ef3e648b93a33b31084f030223dc2d3 +619b4e87d66c772be269e32443bfd768 +f30c876850197446f344597f63299fce +7c8a639e3f8175fd35516a1f03517d8e +b487f19e64626e5982f52f8de4238732 +4b41df592b2c341b9821c2c1b7385607 +f781fa740d9c3148343e1a133ee1235a +e4e7c51802d9156046e746804d2c3960 +3a63be1c6da417534b10a261604797ee +dce7b40962fa4ba57d0c2f3ffd5cd939 +dace0df358316264cdedac24fe984907 +6f6199ca0aeb0aad15eb613c37fd7db6 +cce509a1501aaa3739dadcd1351d83d9 +772877b6930a6f69b717a4d286f70259 +d8f658e3732b7ae095c89d2cfc29fe2c +a5e57567b758e1636bba4bb605598846 +7f0cc4258ce46fb86d310efa337d5dd9 +3c9586b001cc1e61d7df35c4f793f8f1 +94a0e6e24b394725a66b6c9c8ba3072b +4c01c47409d56a9ba57c421ce529b015 +f52cda4db0cd197d5a4577cc3e1d7ed9 +f2c479c08e12dc3db08cc803d0a2a74e +f5dfcc218ae104eba96007de1aceae2e +1a6e97ef60fd4dce42ce071d2e2eacbe +91e4d4e2361fcff17b342f4bccaab971 +9d47703a6842aaac5af6c5d030518e7e +eece44627dbea68991083644fc2381cd +51869793a192ffcf48cf21da662ecc9e +163be4c547f021ca8cb28a846eebf391 +344f9a7106be2be501b8e4058be63db1 +fa2fe8613470850bc170ca1f12b290cd +b8de6ae393a5942f594106575cc6ba9f +14a91b5383dc58a04629135783aa9530 +2eb55d859f402efccaedf26bdd5e7902 +590f580c70c9786da51aa8c51179e56a +74fac2eaf11337dfb26e3526fca79fae +d88e4d50c166be51882d8661fbeccf96 +88af6551d54e4ed178dce48233d2e5c0 +28fa953a8f34f100e579ca2c7009f38d +985fd0061c8219c4696c77f6bf115b5d +bd145b09d36a4fe829198e996fbb5a94 +c0fbb7395a9445007c6f48cfe663d7af +0af2f819e0d26a06f5e2fc093f9c543b +4a618df8ff7b52d9488d55640d455bb3 +83042b532a5feaa5a5330c36c70484b4 +39b3379e8a674abaf69c67fa2d9a309f +2510362eadc9668087a4464bef146a44 +afd98f5903cf76c395904e0bb634b4aa +c8a6d4f5db71bb848292eada0ce9b5c9 +4437d4d02ae857c35cafb4a86a9c819c +b5bc681646a7065caf5a5a3ec93b976a +7a7b91f1c78eba0b5da0203dabd3d972 +b4346ff7c1cb9074e51c7f0ef962e2a0 +d5958a235c658ef6af3177d59a2f980d +047e5c8dbf486d1017e335c38005711d +96a7fd8916f7b2bf5c192b2b52a5566a +5c555d4a1e27a0d72bfa06b1c655cd78 +80d6238d61e3f2281f7567c4816b20b7 +72160290ff45fe6fdb995555508fc592 +4696ab63675996dc280b290f51b8d095 +2d2440a00a59b3ec9edcd09e9899f30b +4493b8a3446daf20f34541bde3c24d41 +0b4bc2afc7e9af942c1dc79fd120862f +1758e3ad8cf7cf842955968ef22c68be +5811fbff95273c5413591fbc96b07a0c +dad847f2b0916baded9b5f219b99c213 +69d82cb277d48c60c9baf260b55c8792 +8c4d98b207141ca2fa344fcc7415d164 +a5421ea000aca629c3185e9270d34c8f +5dda27e72e1d5fff2614b14967aa89db +14e209d94a0bf732aaab43f272825d0e diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp9-superframe.bin b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp9-superframe.bin new file mode 100644 index 00000000..910b5eba Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp9-superframe.bin differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf new file mode 100644 index 00000000..590bdeda Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.crc new file mode 100644 index 00000000..f7997676 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.crc @@ -0,0 +1,13 @@ +eabdf4a9 +6be7a0d4 +b36ef585 +db3e1a59 +d8d2e5cf +db3e1a59 +6eca5ed2 +6eca5ed2 +6f0c30e5 +057e11d3 +2aa4acfc +057e11d3 +2aa4acfc diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.md5 new file mode 100644 index 00000000..2b810310 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.md5 @@ -0,0 +1,13 @@ +eaf1eea9f60fc2fb187bbef2d31af7ce +4a6b0e9f7ea530f5df470692bb217d9e +e72ff5543af78beffb82e385374d258c +34cde815edd7c2e30287879307f6277a +1945558ad45e249c5213a1724e6119e6 +34cde815edd7c2e30287879307f6277a +942648ec667110c846a4c706651fbca8 +942648ec667110c846a4c706651fbca8 +51e6d2b032ddcce42eae15c8ba83656a +d084afaff3d84fe0f9991401be11f144 +706533c04957068d77889bccc95e3037 +d084afaff3d84fe0f9991401be11f144 +706533c04957068d77889bccc95e3037 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf new file mode 100644 index 00000000..c60984c7 Binary files /dev/null and b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf differ diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.crc b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.crc new file mode 100644 index 00000000..96363f15 --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.crc @@ -0,0 +1,16 @@ +ed32c757 +b2b099a4 +02b33fae +a8428354 +44eb0023 +8b577eb0 +09bd1b35 +0c10cfa3 +a8428354 +44eb0023 +8b577eb0 +09bd1b35 +0c10cfa3 +a8428354 +44eb0023 +8b577eb0 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.md5 b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.md5 new file mode 100644 index 00000000..38a6e66d --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.md5 @@ -0,0 +1,16 @@ +9d37524d753e9fa42a0eef64a0059da2 +c64a7219c785b2bc02bdce466d66a363 +49c1deda1b22079af7a27fb79a6d99c3 +5657cc859e68b77964545d9bfe6c8379 +e69ada58184ab59345fe44ee2db4db06 +d4176f8ef863083edd6a01a8e6ed1e18 +524373eaaebc7ce4a1d02b2745a6a74c +3b6ada280133bd5543c8827dd763b055 +5657cc859e68b77964545d9bfe6c8379 +e69ada58184ab59345fe44ee2db4db06 +d4176f8ef863083edd6a01a8e6ed1e18 +524373eaaebc7ce4a1d02b2745a6a74c +3b6ada280133bd5543c8827dd763b055 +5657cc859e68b77964545d9bfe6c8379 +e69ada58184ab59345fe44ee2db4db06 +d4176f8ef863083edd6a01a8e6ed1e18 diff --git a/crates/pf-bitstream/vendor/cros-codecs/src/lib.rs b/crates/pf-bitstream/vendor/cros-codecs/src/lib.rs new file mode 100644 index 00000000..06f6c5aa --- /dev/null +++ b/crates/pf-bitstream/vendor/cros-codecs/src/lib.rs @@ -0,0 +1,80 @@ +// 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. + +//! Vendored cros-codecs **parser layer**: the `codec` module (H.264 / H.265 / AV1 / VP9 +//! parsers, DPBs, picture types) plus `bitstream_utils`. The decoder/encoder/backend +//! halves of upstream are deliberately not vendored — punktfunk's own decode layer +//! (`pf-bitstream`) sits where upstream's `decoder::stateless` would. +//! +//! This lib.rs is the one heavily-trimmed file: upstream's carries the feature-gated +//! backend modules and CLI-facing enums; only the items the `codec` module actually +//! references survive here (`Resolution` and its round mode). Everything below this +//! module doc is copied verbatim from upstream lib.rs. See PROVENANCE.md for the +//! snapshot source and the full list of deviations. + +// Vendored code is not held to the workspace lint bar: CI's `--workspace -- -D warnings` +// clippy leg would otherwise fail on upstream style (27 warnings at vendoring time). +// Held-back lints here, PROVENANCE.md records the posture. +#![allow(clippy::all)] +#![allow(mismatched_lifetime_syntaxes)] + +pub mod bitstream_utils; +pub mod codec; + +/// Rounding modes for `Resolution` +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ResolutionRoundMode { + /// Rounds component-wise to the next even value. + Even, +} + +/// A frame resolution in pixels. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Resolution { + pub width: u32, + pub height: u32, +} + +impl Resolution { + /// Whether `self` can contain `other`. + pub fn can_contain(&self, other: Self) -> bool { + self.width >= other.width && self.height >= other.height + } + + /// Rounds `self` according to `rnd_mode`. + pub fn round(mut self, rnd_mode: ResolutionRoundMode) -> Self { + match rnd_mode { + ResolutionRoundMode::Even => { + if self.width % 2 != 0 { + self.width += 1; + } + + if self.height % 2 != 0 { + self.height += 1; + } + } + } + + self + } + + pub fn get_area(&self) -> usize { + (self.width as usize) * (self.height as usize) + } +} + +impl From<(u32, u32)> for Resolution { + fn from(value: (u32, u32)) -> Self { + Self { + width: value.0, + height: value.1, + } + } +} + +impl From for (u32, u32) { + fn from(value: Resolution) -> Self { + (value.width, value.height) + } +}