| 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)) |
| } |
| } |
| } |