blob: 4f218ff21722dffe6025692cfbb070c9eb205863 [file] [log] [blame]
use crate::FrameError;
use crate::Plane;
use crate::{Frame, PixelBuffer, Spatial, Superblock};
use std::fmt;
/// Where in the codec pipeline a pixel buffer was sampled from.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum PixelType {
/// Pre-encode pixels (may not always be available).
Original,
/// Intra or inter predicted pixels.
Prediction,
/// Reconstructed pixels BEFORE filtering.
PreFiltered,
/// Final reconstructed pixels AFTER filtering.
Reconstruction,
/// Residual, i.e. (PreFiltered - Prediction).
Residual,
/// The effect of the in-loop filtering, i.e. (Reconstruction - PreFiltered).
FilterDelta,
/// (Original - Reconstruction) - depends on Original pixels being available.
Distortion,
}
impl PixelType {
/// Whether this pixel type represents a difference between two other pixel types.
pub fn is_delta(&self) -> bool {
match self {
Self::Original | Self::Prediction | Self::PreFiltered | Self::Reconstruction => false,
Self::Residual | Self::FilterDelta | Self::Distortion => true,
}
}
}
impl PixelBuffer {
/// Retrieves a pixel from the buffer, and compensates for bit_depth adjustment if necessary.
/// `desired_bit_depth` will typically be the bit_depth of the stream itself. The underlying
/// buffer may have a different bit_depth in the case of original YUV pixels.
pub fn get_pixel(&self, x: i32, y: i32, desired_bit_depth: u8) -> Result<i16, FrameError> {
use FrameError::*;
let stride = self.width;
let index = (y * stride + x) as usize;
let mut pixel = *self.pixels.get(index).ok_or_else(|| {
BadPixelBuffer(format!(
"Out of bounds access (x={x}, y={y}) on pixel buffer (width={}, height={}).",
self.width, self.height
))
})?;
if (self.bit_depth as u8) < desired_bit_depth {
let left_shift = desired_bit_depth - self.bit_depth as u8;
pixel <<= left_shift;
}
else if (self.bit_depth as u8) > desired_bit_depth {
let right_shift = self.bit_depth as u8 - desired_bit_depth;
pixel >>= right_shift;
}
Ok(pixel as i16)
}
}
/// Reference to a pixel buffer, or two pixel buffers in the case of a delta pixel type.
#[derive(Debug, Clone)]
pub enum PixelBufferRef<'a> {
Single(&'a PixelBuffer),
Delta(&'a PixelBuffer, &'a PixelBuffer),
}
impl<'a> PixelBufferRef<'a> {
pub fn new_single(buf: &'a PixelBuffer) -> Self {
Self::Single(buf)
}
pub fn new_delta(buf_1: &'a PixelBuffer, buf_2: &'a PixelBuffer) -> Self {
Self::Delta(buf_1, buf_2)
}
/// Assumes both underlying buffers have the same width.
pub fn width(&self) -> i32 {
match self {
Self::Single(buf) => buf.width,
Self::Delta(buf_1, _) => buf_1.width,
}
}
/// Assumes both underlying buffers have the same height.
pub fn height(&self) -> i32 {
match self {
Self::Single(buf) => buf.height,
Self::Delta(buf_1, _) => buf_1.height,
}
}
/// Get a pixel from the underlying buffer(s), or a `FrameError` if OoB access occurs.
pub fn get_pixel(&self, x: i32, y: i32, desired_bit_depth: u8) -> Result<i16, FrameError> {
match self {
Self::Single(buf) => {
buf.get_pixel(x, y, desired_bit_depth)
}
Self::Delta(buf_1, buf_2) => {
let pixel_1 = buf_1.get_pixel(x, y, desired_bit_depth)?;
let pixel_2 = buf_2.get_pixel(x, y, desired_bit_depth)?;
Ok(pixel_1 - pixel_2)
}
}
}
}
impl fmt::Display for PixelType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let text = match self {
PixelType::Original => "Original YUV",
PixelType::Prediction => "Prediction",
PixelType::PreFiltered => "Prefiltered",
PixelType::Reconstruction => "Reconstruction",
PixelType::Residual => "Residual",
PixelType::FilterDelta => "Filter Delta",
PixelType::Distortion => "Distortion",
};
write!(f, "{text}")
}
}
/// Pixel data for a single plane (Y, U or V) and single pixel type.
pub struct PixelPlane {
pub bit_depth: u8,
pub width: i32,
pub height: i32,
pub pixels: Vec<i16>,
pub plane: Plane,
pub pixel_type: PixelType,
}
impl PixelPlane {
fn create_from_tip_frame(frame: &Frame, plane: Plane, pixel_type: PixelType) -> Result<Self, FrameError> {
use FrameError::*;
let tip_params = frame.tip_frame_params.as_ref().unwrap();
let width = plane.subsampled(frame.width());
let height = plane.subsampled(frame.height());
let bit_depth = frame.bit_depth();
let mut pixels = vec![0; (width * height) as usize];
let pixel_data = tip_params
.pixel_data
.get(plane.to_i32() as usize)
.ok_or(BadFrame("Missing pixel data in tip frame.".into()))?;
let pixel_buffer = match pixel_type {
PixelType::Original => {
PixelBufferRef::new_single(pixel_data.original.as_ref().ok_or(MissingPixelBuffer(format!(
"Original pixel data for plane {} not present",
plane.to_usize()
)))?)
}
PixelType::Reconstruction => {
PixelBufferRef::new_single(pixel_data.reconstruction.as_ref().ok_or(MissingPixelBuffer(format!(
"Reconstruction pixel data for plane {} not present",
plane.to_usize()
)))?)
}
PixelType::Distortion => {
let original = pixel_data.original.as_ref().ok_or(MissingPixelBuffer(format!(
"Original pixel data for plane {} not present",
plane.to_usize()
)))?;
let reconstruction = pixel_data.reconstruction.as_ref().ok_or(MissingPixelBuffer(format!(
"Reconstruction pixel data for plane {} not present",
plane.to_usize()
)))?;
PixelBufferRef::new_delta(original, reconstruction)
}
_ => {
return Err(FrameError::Internal(format!(
"Tried to retrieve invalid single pixel buffer type ({pixel_type:?}) from TIP params."
)))
}
};
for y in 0..height {
for x in 0..width {
let index = (y * width + x) as usize;
pixels[index] = pixel_buffer.get_pixel(x, y, bit_depth)?;
}
}
Ok(Self {
bit_depth,
width,
height,
pixels,
plane,
pixel_type,
})
}
fn create_from_superblocks(frame: &Frame, plane: Plane, pixel_type: PixelType) -> Result<Self, FrameError> {
use FrameError::*;
let width = plane.subsampled(frame.width());
let height = plane.subsampled(frame.height());
let bit_depth = frame.bit_depth();
let mut pixels = vec![0; (width * height) as usize];
for sb_ctx in frame.iter_superblocks() {
let sb = sb_ctx.superblock;
let sb_width = plane.subsampled(sb.width());
let sb_height = plane.subsampled(sb.height());
if sb_width <= 0 || sb_height <= 0 {
return Err(BadSuperblock(format!("Invalid dimensions: {sb_width}x{sb_height}")));
}
let sb_x = plane.subsampled(sb.x());
let sb_y = plane.subsampled(sb.y());
if sb_x < 0 || sb_x >= width || sb_y < 0 || sb_y >= height {
return Err(BadSuperblock(format!("Outside frame bounds: x={sb_x}, y={sb_y}")));
}
let remaining_width = width - sb_x;
let remaining_height = height - sb_y;
let cropped_sb_width = sb_width.min(remaining_width);
let cropped_sb_height = sb_height.min(remaining_height);
let pixel_buffer = sb.get_pixels(plane, pixel_type)?;
if cropped_sb_width > pixel_buffer.width() || cropped_sb_height > pixel_buffer.height() {
return Err(BadPixelBuffer(format!(
"Expected pixel buffer shape: ({}x{}), Actual: ({}x{})",
cropped_sb_width,
cropped_sb_height,
pixel_buffer.width(),
pixel_buffer.height(),
)));
}
for rel_y in 0..sb_height {
let abs_y = sb_y + rel_y;
// Clip on frame bottom edge if frame height isn't a multiple of superblock size.
if abs_y >= height {
break;
}
for rel_x in 0..sb_width {
let abs_x = sb_x + rel_x;
// Clip on frame right edge if frame width isn't a multiple of superblock size.
if abs_x >= width {
break;
}
let dest_index = (abs_y * width + abs_x) as usize;
pixels[dest_index] = pixel_buffer.get_pixel(rel_x, rel_y, bit_depth)?;
}
}
}
Ok(Self {
bit_depth,
width,
height,
pixels,
plane,
pixel_type,
})
}
pub fn create_from_frame(frame: &Frame, plane: Plane, pixel_type: PixelType) -> Result<Self, FrameError> {
if let Some(tip_params) = &frame.tip_frame_params {
// TODO(comc): Const for this 2.
if tip_params.tip_mode == 2 {
return Self::create_from_tip_frame(frame, plane, pixel_type);
}
}
Self::create_from_superblocks(frame, plane, pixel_type)
}
}
impl Superblock {
/// Retrieves a single `PixelBuffer` from this superblock.
pub fn get_single_pixel_buffer(&self, plane: Plane, pixel_type: PixelType) -> Result<&PixelBuffer, FrameError> {
use FrameError::*;
let pixel_data = self.pixel_data.get(plane.to_usize()).ok_or(MissingPixelBuffer(format!(
"Pixel data for plane {} not present ({} total)",
plane.to_usize(),
self.pixel_data.len()
)))?;
let pixels = match pixel_type {
PixelType::Original => pixel_data.original.as_ref().ok_or(MissingPixelBuffer(format!(
"Original pixel data for plane {} not present",
plane.to_usize()
)))?,
PixelType::Prediction => pixel_data.prediction.as_ref().ok_or(MissingPixelBuffer(format!(
"Prediction pixel data for plane {} not present",
plane.to_usize()
)))?,
PixelType::PreFiltered => pixel_data.pre_filtered.as_ref().ok_or(MissingPixelBuffer(format!(
"Pre-filtered pixel data for plane {} not present",
plane.to_usize()
)))?,
PixelType::Reconstruction => pixel_data.reconstruction.as_ref().ok_or(MissingPixelBuffer(format!(
"Reconstruction pixel data for plane {} not present",
plane.to_usize()
)))?,
_ => {
return Err(FrameError::Internal(format!(
"Tried to retrieve invalid single pixel buffer type ({pixel_type:?}) from protobuf superblock."
)))
}
};
let width = pixels.width;
let height = pixels.height;
let num_pixels = width * height;
let actual_pixels = pixels.pixels.len() as i32;
if num_pixels != actual_pixels {
return Err(FrameError::BadPixelBuffer(format!(
"Pixel buffer contains {actual_pixels} pixels, but dimensions require {num_pixels} pixels ({}x{})",
width, height
)));
}
Ok(pixels)
}
pub fn get_pixels(&self, plane: Plane, pixel_type: PixelType) -> Result<PixelBufferRef, FrameError> {
if pixel_type.is_delta() {
let (buf_1, buf_2) = match pixel_type {
PixelType::Residual => {
let pre_filtered = self.get_single_pixel_buffer(plane, PixelType::PreFiltered)?;
let prediction = self.get_single_pixel_buffer(plane, PixelType::Prediction)?;
(pre_filtered, prediction)
}
PixelType::FilterDelta => {
let reconstruction = self.get_single_pixel_buffer(plane, PixelType::Reconstruction)?;
let pre_filtered = self.get_single_pixel_buffer(plane, PixelType::PreFiltered)?;
(reconstruction, pre_filtered)
}
PixelType::Distortion => {
let original = self.get_single_pixel_buffer(plane, PixelType::Original)?;
let reconstruction = self.get_single_pixel_buffer(plane, PixelType::Reconstruction)?;
(original, reconstruction)
}
_ => {
return Err(FrameError::Internal(format!(
"Tried to retrieve invalid pixel delta type: {pixel_type:?}"
)));
}
};
if buf_1.width != buf_2.width || buf_1.height != buf_2.height {
return Err(FrameError::BadPixelBuffer(format!(
"Mismatched dimensions: {}x{} vs {}x{}",
buf_1.width, buf_1.height, buf_2.width, buf_2.height
)));
}
Ok(PixelBufferRef::new_delta(buf_1, buf_2))
} else {
let buf = self.get_single_pixel_buffer(plane, pixel_type)?;
Ok(PixelBufferRef::new_single(buf))
}
}
}