Files
effectcraft/crates/hevcenc/src/params.rs
T

263 lines
10 KiB
Rust

//! Parameter sets (7.3.2.1 VPS, 7.3.2.2 SPS with E.2.1 VUI, 7.3.2.3 PPS), profile_tier_level (7.3.3),
//! level selection (Annex A, Table A.8) and the `hvcC` record (ISO/IEC 14496-15 §8.3.3.1).
use crate::bitstream::{BitWriter, NAL_PPS, NAL_SPS, NAL_VPS, nal_unit};
/// Fixed coding structure of the encoder.
pub const LOG2_CTB: u32 = 6;
pub const LOG2_MIN_CB: u32 = 3;
pub const LOG2_MIN_TB: u32 = 2;
pub const LOG2_MAX_TB: u32 = 5;
pub const LOG2_MAX_POC_LSB: u32 = 8;
/// MaxNumMergeCand.
pub const MAX_MERGE_CAND: usize = 5;
/// Sequence-level settings shared by the parameter-set writers.
#[derive(Clone, Debug)]
pub struct SeqParams {
/// Coded (padded) size, multiples of the minimum CB size.
pub coded_width: u32,
pub coded_height: u32,
/// Output (cropped) size.
pub width: u32,
pub height: u32,
pub bit_depth: u32,
pub profile_idc: u8,
pub level_idc: u8,
pub full_range: bool,
pub fps_num: u32,
pub fps_den: u32,
}
/// (level_idc, MaxLumaPs, MaxLumaSr, MaxBR main tier in kbit/s) of Table A.8 (and A.9 for luma rates).
const LEVELS: [(u8, u64, u64, u64); 13] = [
(30, 36_864, 552_960, 128),
(60, 122_880, 3_686_400, 1_500),
(63, 245_760, 7_372_800, 3_000),
(90, 552_960, 16_588_800, 6_000),
(93, 983_040, 33_177_600, 10_000),
(120, 2_228_224, 66_846_720, 12_000),
(123, 2_228_224, 133_693_440, 20_000),
(150, 8_912_896, 267_386_880, 25_000),
(153, 8_912_896, 534_773_760, 40_000),
(156, 8_912_896, 1_069_547_520, 60_000),
(180, 35_651_584, 1_069_547_520, 60_000),
(183, 35_651_584, 2_139_095_040, 120_000),
(186, 35_651_584, 4_278_190_080, 240_000),
];
/// Lowest level whose picture size, sample rate and (when known) bitrate limits fit.
pub fn choose_level(width: u32, height: u32, fps_num: u32, fps_den: u32, kbps: Option<u32>) -> u8 {
let ps = width as u64 * height as u64;
let sr = (ps as f64 * fps_num as f64 / fps_den.max(1) as f64).ceil() as u64;
for &(idc, max_ps, max_sr, max_br) in &LEVELS {
let max_dim = ((max_ps * 8) as f64).sqrt() as u64;
if ps <= max_ps && (width as u64) <= max_dim && (height as u64) <= max_dim && sr <= max_sr && kbps.is_none_or(|k| k as u64 <= max_br) {
return idc;
}
}
186
}
fn profile_tier_level(w: &mut BitWriter, p: &SeqParams) {
w.put(0, 2); // general_profile_space
w.put(0, 1); // general_tier_flag (Main tier)
w.put(p.profile_idc as u32, 5);
w.put(compat_flags(p.profile_idc), 32);
w.put(1, 1); // general_progressive_source_flag
w.put(0, 1); // general_interlaced_source_flag
w.put(0, 1); // general_non_packed_constraint_flag
w.put(1, 1); // general_frame_only_constraint_flag
w.put(0, 32); // general_reserved_zero_43bits ...
w.put(0, 11);
w.put(0, 1); // general_inbld_flag / reserved
w.put(p.level_idc as u32, 8);
// sps_max_sub_layers_minus1 == 0: no sub-layer flags
}
/// general_profile_compatibility_flag[j] as a 32-bit word (flag 0 is the MSB). A Main stream is also
/// Main 10 compatible.
fn compat_flags(profile_idc: u8) -> u32 {
let mut f = 1u32 << (31 - profile_idc as u32);
if profile_idc == 1 {
f |= 1 << (31 - 2);
}
f
}
pub fn vps(p: &SeqParams) -> Vec<u8> {
let mut w = BitWriter::new();
w.put(0, 4); // vps_video_parameter_set_id
w.put(1, 1); // vps_base_layer_internal_flag
w.put(1, 1); // vps_base_layer_available_flag
w.put(0, 6); // vps_max_layers_minus1
w.put(0, 3); // vps_max_sub_layers_minus1
w.put(1, 1); // vps_temporal_id_nesting_flag
w.put(0xffff, 16);
profile_tier_level(&mut w, p);
w.put(1, 1); // vps_sub_layer_ordering_info_present_flag
w.ue(1); // vps_max_dec_pic_buffering_minus1
w.ue(0); // vps_max_num_reorder_pics
w.ue(0); // vps_max_latency_increase_plus1
w.put(0, 6); // vps_max_layer_id
w.ue(0); // vps_num_layer_sets_minus1
w.put(0, 1); // vps_timing_info_present_flag
w.put(0, 1); // vps_extension_flag
w.trailing();
nal_unit(NAL_VPS, &w.into_bytes())
}
pub fn sps(p: &SeqParams) -> Vec<u8> {
let mut w = BitWriter::new();
w.put(0, 4); // sps_video_parameter_set_id
w.put(0, 3); // sps_max_sub_layers_minus1
w.put(1, 1); // sps_temporal_id_nesting_flag
profile_tier_level(&mut w, p);
w.ue(0); // sps_seq_parameter_set_id
w.ue(1); // chroma_format_idc 4:2:0
w.ue(p.coded_width);
w.ue(p.coded_height);
let crop = p.coded_width != p.width || p.coded_height != p.height;
w.flag(crop); // conformance_window_flag
if crop {
// offsets in chroma sample units (SubWidthC = SubHeightC = 2)
w.ue(0);
w.ue((p.coded_width - p.width) / 2);
w.ue(0);
w.ue((p.coded_height - p.height) / 2);
}
w.ue(p.bit_depth - 8); // bit_depth_luma_minus8
w.ue(p.bit_depth - 8); // bit_depth_chroma_minus8
w.ue(LOG2_MAX_POC_LSB - 4);
w.put(1, 1); // sps_sub_layer_ordering_info_present_flag
w.ue(1); // sps_max_dec_pic_buffering_minus1
w.ue(0); // sps_max_num_reorder_pics
w.ue(0); // sps_max_latency_increase_plus1
w.ue(LOG2_MIN_CB - 3);
w.ue(LOG2_CTB - LOG2_MIN_CB);
w.ue(LOG2_MIN_TB - 2);
w.ue(LOG2_MAX_TB - LOG2_MIN_TB);
w.ue(0); // max_transform_hierarchy_depth_inter
w.ue(0); // max_transform_hierarchy_depth_intra
w.put(0, 1); // scaling_list_enabled_flag
w.put(0, 1); // amp_enabled_flag
w.put(0, 1); // sample_adaptive_offset_enabled_flag
w.put(0, 1); // pcm_enabled_flag
w.ue(1); // num_short_term_ref_pic_sets
// st_ref_pic_set(0): one negative picture at delta POC -1, used by the current picture
w.ue(1); // num_negative_pics
w.ue(0); // num_positive_pics
w.ue(0); // delta_poc_s0_minus1
w.put(1, 1); // used_by_curr_pic_s0_flag
w.put(0, 1); // long_term_ref_pics_present_flag
w.put(0, 1); // sps_temporal_mvp_enabled_flag
w.put(1, 1); // strong_intra_smoothing_enabled_flag
w.put(1, 1); // vui_parameters_present_flag
vui(&mut w, p);
w.put(0, 1); // sps_extension_present_flag
w.trailing();
nal_unit(NAL_SPS, &w.into_bytes())
}
fn vui(w: &mut BitWriter, p: &SeqParams) {
w.put(0, 1); // aspect_ratio_info_present_flag
w.put(0, 1); // overscan_info_present_flag
w.put(1, 1); // video_signal_type_present_flag
w.put(5, 3); // video_format: unspecified
w.flag(p.full_range);
w.put(1, 1); // colour_description_present_flag
w.put(1, 8); // colour_primaries BT.709
w.put(1, 8); // transfer_characteristics BT.709
w.put(1, 8); // matrix_coeffs BT.709
w.put(0, 1); // chroma_loc_info_present_flag
w.put(0, 1); // neutral_chroma_indication_flag
w.put(0, 1); // field_seq_flag
w.put(0, 1); // frame_field_info_present_flag
w.put(0, 1); // default_display_window_flag
w.put(1, 1); // vui_timing_info_present_flag
w.put(p.fps_den, 32); // vui_num_units_in_tick
w.put(p.fps_num, 32); // vui_time_scale
w.put(0, 1); // vui_poc_proportional_to_timing_flag
w.put(0, 1); // vui_hrd_parameters_present_flag
w.put(0, 1); // bitstream_restriction_flag
}
pub fn pps() -> Vec<u8> {
let mut w = BitWriter::new();
w.ue(0); // pps_pic_parameter_set_id
w.ue(0); // pps_seq_parameter_set_id
w.put(0, 1); // dependent_slice_segments_enabled_flag
w.put(0, 1); // output_flag_present_flag
w.put(0, 3); // num_extra_slice_header_bits
w.put(1, 1); // sign_data_hiding_enabled_flag
w.put(0, 1); // cabac_init_present_flag
w.ue(0); // num_ref_idx_l0_default_active_minus1
w.ue(0); // num_ref_idx_l1_default_active_minus1
w.se(0); // init_qp_minus26
w.put(0, 1); // constrained_intra_pred_flag
w.put(0, 1); // transform_skip_enabled_flag
w.put(0, 1); // cu_qp_delta_enabled_flag
w.se(0); // pps_cb_qp_offset
w.se(0); // pps_cr_qp_offset
w.put(0, 1); // pps_slice_chroma_qp_offsets_present_flag
w.put(0, 1); // weighted_pred_flag
w.put(0, 1); // weighted_bipred_flag
w.put(0, 1); // transquant_bypass_enabled_flag
w.put(0, 1); // tiles_enabled_flag
w.put(0, 1); // entropy_coding_sync_enabled_flag
w.put(0, 1); // pps_loop_filter_across_slices_enabled_flag
w.put(1, 1); // deblocking_filter_control_present_flag
w.put(0, 1); // deblocking_filter_override_enabled_flag
w.put(0, 1); // pps_deblocking_filter_disabled_flag
w.se(0); // pps_beta_offset_div2
w.se(0); // pps_tc_offset_div2
w.put(0, 1); // pps_scaling_list_data_present_flag
w.put(0, 1); // lists_modification_present_flag
w.ue(0); // log2_parallel_merge_level_minus2
w.put(0, 1); // slice_segment_header_extension_present_flag
w.put(0, 1); // pps_extension_present_flag
w.trailing();
nal_unit(NAL_PPS, &w.into_bytes())
}
/// HEVCDecoderConfigurationRecord with one VPS, SPS and PPS (lengthSizeMinusOne = 3).
pub fn hvcc(p: &SeqParams, sets: &[Vec<u8>; 3]) -> Vec<u8> {
let mut o = vec![1u8]; // configurationVersion
o.push(p.profile_idc); // profile_space 0, tier 0, profile_idc
o.extend_from_slice(&compat_flags(p.profile_idc).to_be_bytes());
// general_constraint_indicator_flags (48 bits): progressive, interlaced, non_packed, frame_only
o.extend_from_slice(&[0x90, 0, 0, 0, 0, 0]);
o.push(p.level_idc);
o.extend_from_slice(&[0xf0, 0x00]); // reserved + min_spatial_segmentation_idc 0
o.push(0xfc); // reserved + parallelismType 0
o.push(0xfc | 1); // reserved + chroma_format_idc 1
o.push(0xf8 | (p.bit_depth - 8) as u8);
o.push(0xf8 | (p.bit_depth - 8) as u8);
o.extend_from_slice(&[0, 0]); // avgFrameRate unspecified
// constantFrameRate 0, numTemporalLayers 1, temporalIdNested 1, lengthSizeMinusOne 3
o.push((1 << 3) | (1 << 2) | 3);
o.push(3); // numOfArrays
for (set, ty) in sets.iter().zip([NAL_VPS, NAL_SPS, NAL_PPS]) {
o.push(0x80 | ty); // array_completeness 1
o.extend_from_slice(&1u16.to_be_bytes());
o.extend_from_slice(&(set.len() as u16).to_be_bytes());
o.extend_from_slice(set);
}
o
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn levels() {
assert_eq!(choose_level(1920, 1080, 30, 1, None), 120);
assert_eq!(choose_level(1920, 1080, 60, 1, None), 123);
assert_eq!(choose_level(3840, 2160, 30, 1, None), 150);
assert_eq!(choose_level(1280, 720, 30, 1, None), 93);
assert_eq!(choose_level(64, 64, 30, 1, None), 30);
assert_eq!(choose_level(1920, 1080, 30, 1, Some(15_000)), 123);
}
}