From 6911e865c9d26e5472703e7700b3c83996b65e27 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Thu, 30 Jan 2025 17:45:25 +0530 Subject: [PATCH] net/hlsmultivariantsink: Drop cros-codecs as a dependency This copies over the required bits of code from cros-codecs and drops the dependency on cros-codecs. Part-of: --- Cargo.lock | 24 +- net/hlsmultivariantsink/Cargo.toml | 9 +- .../src/cros_codecs/LICENSE | 26 + .../src/cros_codecs/README.md | 1 + .../src/cros_codecs/h264/mod.rs | 3 + .../src/cros_codecs/h264/nalu.rs | 107 + .../src/cros_codecs/h264/nalu_reader.rs | 180 ++ .../src/cros_codecs/h264/parser.rs | 1231 +++++++++ .../src/cros_codecs/h265/mod.rs | 1 + .../src/cros_codecs/h265/parser.rs | 2440 +++++++++++++++++ .../src/cros_codecs/mod.rs | 2 + net/hlsmultivariantsink/src/imp.rs | 60 +- net/hlsmultivariantsink/src/lib.rs | 1 + .../tests/hlsmultivariantsink.rs | 2 - typos.toml | 1 + 15 files changed, 4012 insertions(+), 76 deletions(-) create mode 100644 net/hlsmultivariantsink/src/cros_codecs/LICENSE create mode 100644 net/hlsmultivariantsink/src/cros_codecs/README.md create mode 100644 net/hlsmultivariantsink/src/cros_codecs/h264/mod.rs create mode 100644 net/hlsmultivariantsink/src/cros_codecs/h264/nalu.rs create mode 100644 net/hlsmultivariantsink/src/cros_codecs/h264/nalu_reader.rs create mode 100644 net/hlsmultivariantsink/src/cros_codecs/h264/parser.rs create mode 100644 net/hlsmultivariantsink/src/cros_codecs/h265/mod.rs create mode 100644 net/hlsmultivariantsink/src/cros_codecs/h265/parser.rs create mode 100644 net/hlsmultivariantsink/src/cros_codecs/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 5d80303b9..099c0f8b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1342,23 +1342,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "cros-codecs" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4862478feb3de946be3e4b7f316b18957a45d88d1c3695c606bdf6e76a2d2bfb" -dependencies = [ - "anyhow", - "bitreader", - "byteorder", - "bytes", - "crc32fast", - "enumn", - "log", - "nix", - "thiserror 1.0.69", -] - [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -2730,8 +2713,11 @@ name = "gst-plugin-hlsmultivariantsink" version = "0.14.0-alpha.1" dependencies = [ "anyhow", + "bitreader", + "byteorder", + "bytes", "chrono", - "cros-codecs", + "enumn", "gio", "gst-plugin-fmp4", "gst-plugin-hlssink3", @@ -2743,7 +2729,7 @@ dependencies = [ "gstreamer-pbutils", "gstreamer-video", "m3u8-rs", - "sprintf", + "thiserror 2.0.11", ] [[package]] diff --git a/net/hlsmultivariantsink/Cargo.toml b/net/hlsmultivariantsink/Cargo.toml index 05e401fa9..d3e0f6a57 100644 --- a/net/hlsmultivariantsink/Cargo.toml +++ b/net/hlsmultivariantsink/Cargo.toml @@ -14,10 +14,13 @@ gst-app.workspace = true gio.workspace = true m3u8-rs = "5.0" chrono = "0.4" -sprintf = "0.2" gst-pbutils = { workspace = true, features = ["v1_22"] } -[target.'cfg(target_os = "linux")'.dependencies] -cros-codecs = { version = "0.0.5", default-features = false } +anyhow = "1" +bitreader = "0.3" +bytes = "1" +byteorder = "1.5" +enumn = "0.1" +thiserror = "2" [dev-dependencies] gst-audio.workspace = true diff --git a/net/hlsmultivariantsink/src/cros_codecs/LICENSE b/net/hlsmultivariantsink/src/cros_codecs/LICENSE new file mode 100644 index 000000000..9c1f83947 --- /dev/null +++ b/net/hlsmultivariantsink/src/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/net/hlsmultivariantsink/src/cros_codecs/README.md b/net/hlsmultivariantsink/src/cros_codecs/README.md new file mode 100644 index 000000000..cd2f89ab0 --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/README.md @@ -0,0 +1 @@ +The code here has been taken from [cros-codecs](https://android.googlesource.com/platform/system/cros-codecs/) with the GitHub mirror being [here](https://github.com/chromeos/cros-codecs). Only the required bits for parsing of SPS in H264/H265 has been copied. Copied code is from the released version on crates.io at the time of this writing was v0.0.5. diff --git a/net/hlsmultivariantsink/src/cros_codecs/h264/mod.rs b/net/hlsmultivariantsink/src/cros_codecs/h264/mod.rs new file mode 100644 index 000000000..8c5b98ce7 --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/h264/mod.rs @@ -0,0 +1,3 @@ +pub mod nalu; +pub mod nalu_reader; +pub mod parser; diff --git a/net/hlsmultivariantsink/src/cros_codecs/h264/nalu.rs b/net/hlsmultivariantsink/src/cros_codecs/h264/nalu.rs new file mode 100644 index 000000000..4c3fff36e --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/h264/nalu.rs @@ -0,0 +1,107 @@ +// 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 anyhow::anyhow; +use bytes::Buf; + +#[allow(clippy::len_without_is_empty)] +pub trait Header: Sized { + /// Parse the NALU header, returning it. + fn parse>(cursor: &Cursor) -> anyhow::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]>) -> anyhow::Result> { + let bitstream = cursor.clone().into_inner(); + let pos = usize::try_from(cursor.position())?; + + // 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(anyhow!("No NAL found")), + }; + + 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)?); + + 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 => cursor.chunk().len(), // 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]) + } +} + +impl AsRef<[u8]> for Nalu<'_, U> { + fn as_ref(&self) -> &[u8] { + &self.data[self.offset..self.offset + self.size] + } +} diff --git a/net/hlsmultivariantsink/src/cros_codecs/h264/nalu_reader.rs b/net/hlsmultivariantsink/src/cros_codecs/h264/nalu_reader.rs new file mode 100644 index 000000000..4fb06c24f --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/h264/nalu_reader.rs @@ -0,0 +1,180 @@ +// 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::io::Cursor; + +use anyhow::anyhow; +use anyhow::Context; +use byteorder::ReadBytesExt; +use thiserror::Error; + +/// A bit reader for h264 bitstreams. It properly handles emulation-prevention +/// bytes and stop bits. +pub(crate) struct NaluReader<'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 epb detection. + prev_two_bytes: u16, + /// Number of epbs (i.e. 0x000003) we found. + num_epb: usize, +} + +#[derive(Debug, Error)] +pub(crate) enum GetByteError { + #[error("reader ran out of bits")] + OutOfBits, +} + +#[derive(Debug, Error)] +pub(crate) enum ReadBitsError { + #[error("more than 31 ({0}) bits were requested")] + TooManyBytesRequested(usize), + #[error("failed to advance the current byte")] + GetByte(#[from] GetByteError), + #[error("failed to convert read input to target type")] + ConversionFailed, +} + +impl<'a> NaluReader<'a> { + pub fn new(data: &'a [u8]) -> 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(), + } + } + + /// 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. + pub fn read_bits>(&mut self, num_bits: usize) -> Result { + if num_bits > 31 { + return Err(ReadBitsError::TooManyBytesRequested(num_bits)); + } + + 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.update_curr_byte()?; + } + + 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; + + U::try_from(out).map_err(|_| ReadBitsError::ConversionFailed) + } + + /// Skip `num_bits` bits from the stream. + pub fn skip_bits(&mut self, mut num_bits: usize) -> Result<(), ReadBitsError> { + while num_bits > 0 { + let n = std::cmp::min(num_bits, 31); + self.read_bits::(n)?; + num_bits -= n; + } + + Ok(()) + } + + pub fn read_ue>(&mut self) -> anyhow::Result { + let mut num_bits = 0; + + while self.read_bits::(1)? == 0 { + num_bits += 1; + } + + if num_bits > 31 { + return Err(anyhow!("invalid stream")); + } + + let value = ((1u32 << num_bits) - 1) + .checked_add(self.read_bits::(num_bits)?) + .context("read number cannot fit in 32 bits")?; + + U::try_from(value).map_err(|_| anyhow!("conversion error")) + } + + pub fn read_ue_bounded>(&mut self, min: u32, max: u32) -> anyhow::Result { + let ue = self.read_ue()?; + if ue > max || ue < min { + Err(anyhow!( + "Value out of bounds: expected {} - {}, got {}", + min, + max, + ue + )) + } else { + Ok(U::try_from(ue).map_err(|_| anyhow!("Conversion error"))?) + } + } + + pub fn read_ue_max>(&mut self, max: u32) -> anyhow::Result { + self.read_ue_bounded(0, max) + } + + pub fn read_se>(&mut self) -> anyhow::Result { + let ue = self.read_ue::()? as i32; + + if ue % 2 == 0 { + Ok(U::try_from(-ue / 2).map_err(|_| anyhow!("Conversion error"))?) + } else { + Ok(U::try_from(ue / 2 + 1).map_err(|_| anyhow!("Conversion error"))?) + } + } + + pub fn read_se_bounded>(&mut self, min: i32, max: i32) -> anyhow::Result { + let se = self.read_se()?; + if se < min || se > max { + Err(anyhow!( + "Value out of bounds, expected between {}-{}, got {}", + min, + max, + se + )) + } else { + Ok(U::try_from(se).map_err(|_| anyhow!("Conversion error"))?) + } + } + + fn get_byte(&mut self) -> Result { + self.data.read_u8().map_err(|_| GetByteError::OutOfBits) + } + + fn update_curr_byte(&mut self) -> Result<(), GetByteError> { + let mut byte = self.get_byte()?; + + 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.num_remaining_bits_in_curr_byte = 8; + self.prev_two_bytes = (self.prev_two_bytes << 8) | u16::from(byte); + + self.curr_byte = byte; + Ok(()) + } +} diff --git a/net/hlsmultivariantsink/src/cros_codecs/h264/parser.rs b/net/hlsmultivariantsink/src/cros_codecs/h264/parser.rs new file mode 100644 index 000000000..6297aee03 --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/h264/parser.rs @@ -0,0 +1,1231 @@ +// 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::collections::BTreeMap; +use std::io::Cursor; +use std::rc::Rc; + +use anyhow::anyhow; +use anyhow::Context; +use bytes::Buf; +use enumn::N; + +use crate::cros_codecs::h264::nalu; +use crate::cros_codecs::h264::nalu::Header; +use crate::cros_codecs::h264::nalu_reader::NaluReader; + +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_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(N, 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, +} + +#[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], +} + +#[derive(N, 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, +} + +/// 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)), + ) + } + } + } +} + +// 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(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, +} + +#[derive(Debug, Default)] +pub struct Parser { + active_spses: 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 NaluReader, + scaling_list: &mut U, + use_default: &mut bool, + ) -> anyhow::Result<()> { + // 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 NaluReader, sps: &mut Sps) -> anyhow::Result<()> { + 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_hrd(r: &mut NaluReader, hrd: &mut HrdParams) -> anyhow::Result<()> { + 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 NaluReader, sps: &mut Sps) -> anyhow::Result<()> { + 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(anyhow!( + "num_units_in_tick == 0, which is not allowed by E.2.1" + )); + } + + vui.time_scale = r.read_bits::(31)? << 1; + vui.time_scale |= r.read_bit()? as u32; + if vui.time_scale == 0 { + return Err(anyhow!("time_scale == 0, which is not allowed by E.2.1")); + } + + 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) -> anyhow::Result<&Rc> { + if !matches!(nalu.header.type_, NaluType::Sps) { + return Err(anyhow!( + "Invalid NALU type, expected {:?}, got {:?}", + NaluType::Sps, + nalu.header.type_ + )); + } + + let data = nalu.as_ref(); + // Skip the header + let mut r = NaluReader::new(&data[nalu.header.len()..]); + 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::n(level).with_context(|| format!("Unsupported level {}", 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(anyhow!("Invalid frame crop width"))?; + + 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(anyhow!("invalid frame crop height"))?; + } + + 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(anyhow!( + "Broken data: Number of active SPSs > MAX_SPS_COUNT" + )); + } + + let sps = Rc::new(sps); + self.active_spses.remove(&key); + Ok(self.active_spses.entry(key).or_insert(sps)) + } +} + +#[derive(Debug)] +pub struct NaluHeader { + pub type_: NaluType, +} + +impl Header for NaluHeader { + fn parse>(cursor: &Cursor) -> anyhow::Result { + if !cursor.has_remaining() { + return Err(anyhow!("Broken Data")); + } + + let byte = cursor.chunk()[0]; + + let type_ = NaluType::n(byte & 0x1f).ok_or(anyhow!("Broken Data"))?; + + if let NaluType::SliceExt = type_ { + return Err(anyhow!("Stream contain unsupported/unimplemented NALs")); + } + + Ok(NaluHeader { type_ }) + } + + fn is_end(&self) -> bool { + matches!(self.type_, NaluType::SeqEnd | NaluType::StreamEnd) + } + + fn len(&self) -> usize { + 1 + } +} diff --git a/net/hlsmultivariantsink/src/cros_codecs/h265/mod.rs b/net/hlsmultivariantsink/src/cros_codecs/h265/mod.rs new file mode 100644 index 000000000..67c567fa0 --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/h265/mod.rs @@ -0,0 +1 @@ +pub mod parser; diff --git a/net/hlsmultivariantsink/src/cros_codecs/h265/parser.rs b/net/hlsmultivariantsink/src/cros_codecs/h265/parser.rs new file mode 100644 index 000000000..a161a576b --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/h265/parser.rs @@ -0,0 +1,2440 @@ +// 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. + +//! An Annex B h.265 parser. +//! +//! Parses SPSs from NALUs. + +use std::collections::BTreeMap; + +use anyhow::anyhow; +use anyhow::Context; +use bitreader::BitReader; +use bytes::Buf; +use enumn::N; + +use crate::cros_codecs::h264::nalu; +use crate::cros_codecs::h264::nalu::Header; +use crate::cros_codecs::h264::nalu_reader::NaluReader; + +// Given the max SPS id. +const MAX_SPS_COUNT: usize = 16; + +// 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(N, 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, +} + +#[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 Header for NaluHeader { + fn parse>(cursor: &std::io::Cursor) -> anyhow::Result { + let data = &cursor.chunk()[0..2]; + let mut r = BitReader::new(data); + + // Skip forbidden_zero_bit + r.skip(1)?; + + Ok(Self { + type_: NaluType::n(r.read_u32(6)?).ok_or(anyhow!("Invalid NALU type"))?, + nuh_layer_id: r.read_u8(6)?, + nuh_temporal_id_plus1: r.read_u8(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(N, 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, +} + +#[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], +} + +#[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, +} + +#[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 nuh_temporal_id_plus1 - 1 of the associated NALU. + pub temporal_id: u8, +} + +impl Default for Pps { + fn default() -> Self { + Self { + pic_parameter_set_id: Default::default(), + seq_parameter_set_id: Default::default(), + 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(), + temporal_id: Default::default(), + } + } +} + +#[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, 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_spses: BTreeMap, +} + +impl Parser { + fn parse_profile_tier_level( + ptl: &mut ProfileTierLevel, + r: &mut NaluReader, + profile_present_flag: bool, + sps_max_sub_layers_minus_1: u8, + ) -> anyhow::Result<()> { + 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::n(level).with_context(|| format!("Unsupported level {}", 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::n(level).with_context(|| format!("Unsupported level {}", 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 NaluReader) -> anyhow::Result<()> { + // 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(anyhow!("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(anyhow!("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(anyhow!("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 NaluReader, + st_rps_idx: u8, + ) -> anyhow::Result<()> { + 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(anyhow!("Invalid ref_rps_idx"))?; + + 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 NaluReader, + ) -> anyhow::Result<()> { + 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 NaluReader, + ) -> anyhow::Result<()> { + 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 NaluReader) -> anyhow::Result<()> { + 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 NaluReader) -> anyhow::Result<()> { + 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 NaluReader) -> anyhow::Result<()> { + 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) -> anyhow::Result<&Sps> { + if !matches!(nalu.header.type_, NaluType::SpsNut) { + return Err(anyhow!( + "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 = NaluReader::new(&data[hdr_len..]); + + let mut sps = Sps { + video_parameter_set_id: r.read_bits(4)?, + max_sub_layers_minus1: r.read_bits(3)?, + temporal_id_nesting_flag: r.read_bit()?, + ..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(anyhow!( + "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(anyhow!("Multilayer extension not supported.")); + } + + let three_d_extension_flag = r.read_bit()?; + if three_d_extension_flag { + return Err(anyhow!("3D extension not supported.")); + } + + 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; + + let key = sps.seq_parameter_set_id; + self.active_spses.insert(key, sps); + + if self.active_spses.keys().len() > MAX_SPS_COUNT { + return Err(anyhow!( + "Broken data: Number of active SPSs > MAX_SPS_COUNT" + )); + } + + Ok(self.get_sps(key).unwrap()) + } + + /// Returns a previously parsed sps given `sps_id`, if any. + pub fn get_sps(&self, sps_id: u8) -> Option<&Sps> { + self.active_spses.get(&sps_id) + } +} diff --git a/net/hlsmultivariantsink/src/cros_codecs/mod.rs b/net/hlsmultivariantsink/src/cros_codecs/mod.rs new file mode 100644 index 000000000..66b652d09 --- /dev/null +++ b/net/hlsmultivariantsink/src/cros_codecs/mod.rs @@ -0,0 +1,2 @@ +pub mod h264; +pub mod h265; diff --git a/net/hlsmultivariantsink/src/imp.rs b/net/hlsmultivariantsink/src/imp.rs index b3750b67f..1d2018423 100644 --- a/net/hlsmultivariantsink/src/imp.rs +++ b/net/hlsmultivariantsink/src/imp.rs @@ -1308,12 +1308,10 @@ impl HlsMultivariantSink { drop(state); } - #[cfg(target_os = "linux")] fn parse_h264_sps(&self, buffer: &[u8], group_id: String) { - use cros_codecs::codec::h264::parser as H264Parser; + use crate::cros_codecs::h264::parser as H264Parser; use std::io::Cursor; - let mut caps_codec_str: Option = None; let mut cursor = Cursor::new(buffer); let mut parser = H264Parser::Parser::default(); @@ -1340,34 +1338,19 @@ impl HlsMultivariantSink { constraint_flags += 2u32.pow(7 - index as u32) * (*bit) as u32; }); - match sprintf::sprintf!("avc1.%02X%02X%02X", profile, constraint_flags, level) { - Ok(codec_str) => { - caps_codec_str = Some(codec_str); - } - Err(e) => { - gst::element_error!( - self.obj(), - gst::ResourceError::Failed, - ["Failed to format codec string with error: {:?}", e] - ); - } - } + let codec_str = format!("avc1.{profile:02X}{constraint_flags:02X}{level:02X}"); + + self.build_codec_str_and_write_multivariant_playlist(codec_str, group_id); break; } } - - if let Some(codec_str) = caps_codec_str { - self.build_codec_str_and_write_multivariant_playlist(codec_str, group_id); - } } - #[cfg(target_os = "linux")] fn parse_h265_sps(&self, buffer: &[u8], group_id: String) { - use cros_codecs::codec::h265::parser as H265Parser; + use crate::cros_codecs::h265::parser as H265Parser; use std::io::Cursor; - let mut caps_codec_str: Option = None; let mut cursor = Cursor::new(buffer); let mut parser = H265Parser::Parser::default(); @@ -1429,37 +1412,15 @@ impl HlsMultivariantSink { constraint_indicator_flag += 2u32.pow(7 - index as u32) * (*bit) as u32; }); - match sprintf::sprintf!( - "%s.%X.%hu.%c%d.%02X", - codec_str, - profile_idc, - compat_flag_parameter, - tier_flag, - level_idc, - constraint_indicator_flag - ) { - Ok(codec_str) => { - caps_codec_str = Some(codec_str); - } - Err(e) => { - gst::element_error!( - self.obj(), - gst::ResourceError::Failed, - ["Failed to format codec string with error: {:?}", e] - ); - } - } + let codec_str = format!("{codec_str}.{profile_idc:X}.{compat_flag_parameter}.{tier_flag}{level_idc}.{constraint_indicator_flag:02X}"); + + self.build_codec_str_and_write_multivariant_playlist(codec_str, group_id); break; } } - - if let Some(codec_str) = caps_codec_str { - self.build_codec_str_and_write_multivariant_playlist(codec_str, group_id); - } } - #[cfg(target_os = "linux")] fn parse_sps( &self, caps: &gst::Caps, @@ -1510,12 +1471,7 @@ impl HlsMultivariantSink { * with SPS. codec-utils helpers cannot give us codec string * without the codec_data. For MPEG-TS, parse SPS and figure * out the relevant information to generate codec string. - * - * parse_sps function relies on cros_codecs which supports - * only linux. On other platforms for MPEG-TS, users need to - * specify codec string manually. */ - #[cfg(target_os = "linux")] { let settings = self.settings.lock().unwrap(); let is_mpegts = settings.muxer_type == HlsMultivariantSinkMuxerType::MpegTs; diff --git a/net/hlsmultivariantsink/src/lib.rs b/net/hlsmultivariantsink/src/lib.rs index eba37e035..008cdeddc 100644 --- a/net/hlsmultivariantsink/src/lib.rs +++ b/net/hlsmultivariantsink/src/lib.rs @@ -15,6 +15,7 @@ use gst::glib; use gst::prelude::*; +mod cros_codecs; mod imp; #[derive(Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)] diff --git a/net/hlsmultivariantsink/tests/hlsmultivariantsink.rs b/net/hlsmultivariantsink/tests/hlsmultivariantsink.rs index 0559f6f4d..db9575b4c 100644 --- a/net/hlsmultivariantsink/tests/hlsmultivariantsink.rs +++ b/net/hlsmultivariantsink/tests/hlsmultivariantsink.rs @@ -812,7 +812,6 @@ low/video.m3u8 Ok(()) } -#[cfg(target_os = "linux")] #[test] fn hlsmultivariantsink_multiple_audio_rendition_multiple_video_variant_with_mpegts( ) -> Result<(), ()> { @@ -988,7 +987,6 @@ low/video.m3u8 Ok(()) } -#[cfg(target_os = "linux")] #[test] fn hlsmultivariantsink_multiple_video_variant_with_mpegts_audio_video_muxed() -> Result<(), ()> { init(); diff --git a/typos.toml b/typos.toml index 4e04b259f..122d08526 100644 --- a/typos.toml +++ b/typos.toml @@ -18,6 +18,7 @@ Ines = "Ines" # net/hlsmultivariantsink cros = "cros" +ue = "ue" [files] extend-exclude = [