#![forbid(unsafe_code)]
use crate::DecodeError;
use mediaway_sw::h264::BitReader;
use super::bits::ns;
const MAX_TILE_WIDTH: u32 = 4096;
const MAX_TILE_AREA: u32 = 4096 * 2304;
const MAX_TILE_COLS: u32 = 64;
const MAX_TILE_ROWS: u32 = 64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct TileInfo {
pub(super) sb_cols: u32,
pub(super) sb_rows: u32,
pub(super) context_update_tile_id: u16,
pub(super) uniform_tile_spacing_flag: bool,
}
const fn tile_log2(blk_size: u32, target: u32) -> u32 {
let mut k = 0;
while (blk_size << k) < target {
k += 1;
}
k
}
pub(super) fn parse(
r: &mut BitReader<'_>,
use_128x128_superblock: bool,
mi_cols: u32,
mi_rows: u32,
) -> Result<TileInfo, DecodeError> {
let map_err = |_| DecodeError::InvalidInput;
let sb_shift = if use_128x128_superblock { 5 } else { 4 };
let sb_size = sb_shift + 2;
let sb_cols = if use_128x128_superblock {
(mi_cols + 31) >> 5
} else {
(mi_cols + 15) >> 4
};
let sb_rows = if use_128x128_superblock {
(mi_rows + 31) >> 5
} else {
(mi_rows + 15) >> 4
};
let max_tile_width_sb = MAX_TILE_WIDTH >> sb_size;
let max_tile_area_sb = MAX_TILE_AREA >> (2 * sb_size);
let min_log2_tile_cols = tile_log2(max_tile_width_sb, sb_cols);
let max_log2_tile_cols = tile_log2(1, sb_cols.min(MAX_TILE_COLS));
let max_log2_tile_rows = tile_log2(1, sb_rows.min(MAX_TILE_ROWS));
let min_log2_tiles =
min_log2_tile_cols.max(tile_log2(max_tile_area_sb, sb_rows.saturating_mul(sb_cols)));
let uniform_tile_spacing_flag = r.read_bit().map_err(map_err)? != 0;
let (tile_cols, tile_cols_log2) = if uniform_tile_spacing_flag {
let mut tile_cols_log2 = min_log2_tile_cols;
while tile_cols_log2 < max_log2_tile_cols {
if r.read_bit().map_err(map_err)? == 0 {
break;
}
tile_cols_log2 += 1;
}
(1u32 << tile_cols_log2, tile_cols_log2)
} else {
let mut start_sb = 0u32;
let mut tile_cols = 0u32;
while start_sb < sb_cols {
let max_width = (sb_cols - start_sb).min(max_tile_width_sb);
let size_sb = ns(r, max_width)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?;
start_sb = start_sb
.checked_add(size_sb)
.ok_or(DecodeError::InvalidInput)?;
tile_cols = tile_cols.checked_add(1).ok_or(DecodeError::InvalidInput)?;
}
(tile_cols, tile_log2(1, tile_cols))
};
if tile_cols != 1 {
return Err(DecodeError::Unsupported);
}
let (tile_rows, tile_rows_log2) = if uniform_tile_spacing_flag {
let min_log2_tile_rows = min_log2_tiles.saturating_sub(tile_cols_log2);
let mut tile_rows_log2 = min_log2_tile_rows;
while tile_rows_log2 < max_log2_tile_rows {
if r.read_bit().map_err(map_err)? == 0 {
break;
}
tile_rows_log2 += 1;
}
(1u32 << tile_rows_log2, tile_rows_log2)
} else {
let widest_tile_sb = sb_cols.max(1);
let row_budget = if min_log2_tiles > 0 {
(sb_rows.saturating_mul(sb_cols)) >> (min_log2_tiles + 1)
} else {
sb_rows.saturating_mul(sb_cols)
};
let max_tile_height_sb = (row_budget / widest_tile_sb).max(1);
let mut start_sb = 0u32;
let mut tile_rows = 0u32;
while start_sb < sb_rows {
let max_height = (sb_rows - start_sb).min(max_tile_height_sb);
let size_sb = ns(r, max_height)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?;
start_sb = start_sb
.checked_add(size_sb)
.ok_or(DecodeError::InvalidInput)?;
tile_rows = tile_rows.checked_add(1).ok_or(DecodeError::InvalidInput)?;
}
(tile_rows, tile_log2(1, tile_rows))
};
if tile_rows != 1 {
return Err(DecodeError::Unsupported);
}
let context_update_tile_id = if tile_cols_log2 > 0 || tile_rows_log2 > 0 {
let bits = tile_rows_log2
.checked_add(tile_cols_log2)
.ok_or(DecodeError::InvalidInput)?;
let id = r.read_bits(bits).map_err(map_err)?;
let _tile_size_bytes_minus_1 = r.read_bits(2).map_err(map_err)?;
u16::try_from(id).map_err(|_| DecodeError::InvalidInput)?
} else {
0
};
Ok(TileInfo {
sb_cols,
sb_rows,
context_update_tile_id,
uniform_tile_spacing_flag,
})
}
#[cfg(test)]
#[path = "tile_info_tests.rs"]
mod tests;