// 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. //! Utility functions used by several parts of this crate. //! //! This module is for anything that doesn't fit into the other top-level modules. Try not to add //! new code here unless it really doesn't belong anywhere else. use std::marker::Copy; use std::ops::Add; use std::ops::Div; use std::ops::Mul; use std::ops::Sub; use std::os::fd::OwnedFd; use crate::Fourcc; use crate::FrameLayout; use crate::PlaneLayout; use crate::Resolution; pub fn align_up(x: T, alignment: T) -> T where T: Add + Sub + Div + Mul + From + Copy, { ((x + alignment - T::from(1)) / alignment) * alignment } // This is the formula we use to approximate the maximum compressed buffer size for a video frame. pub fn buffer_size_for_area(width: u32, height: u32) -> u32 { let area = width * height; let mut buffer_size: u32 = 1024 * 1024; if area > 720 * 480 { buffer_size *= 2; } if area > 1920 * 1080 { buffer_size *= 2; } buffer_size } /// A structure that holds user-allocated memory for a frame as well as its layout. #[derive(Debug)] pub struct UserPtrFrame { pub buffers: Vec<*mut u8>, pub mem_layout: std::alloc::Layout, pub layout: FrameLayout, } impl UserPtrFrame { /// Allocate enough memory to back a NV12 frame of `size` dimension. pub fn new_nv12(size: Resolution) -> Self { /// Add what is needed to a value in order to make it a multiple of some alignment. macro_rules! align { ($n:expr, $r:expr) => { ($n + ($r - 1)) & !($r - 1) }; } // Align according to VAAPI constraints. let width = align!(size.width, 16) as usize; let height = align!(size.height, 4) as usize; let stride = align!(width, 64); let uv_start = height * stride; let uv_size = (height / 2) * stride; Self::alloc( FrameLayout { format: (Fourcc::from(b"NV12"), 0), size: Resolution::from((width as u32, height as u32)), planes: vec![ PlaneLayout { buffer_index: 0, offset: 0, stride }, PlaneLayout { buffer_index: 0, offset: uv_start, stride }, ], }, uv_start.max(uv_size), ) } pub fn alloc(layout: FrameLayout, buffer_size: usize) -> Self { let buffer_count = layout .planes .iter() .map(|plane| plane.buffer_index) .collect::>() .len(); // SAFETY: the invariants of `Layout` are respected. let mem_layout = unsafe { std::alloc::Layout::from_size_align_unchecked(buffer_size, 4096) }; let buffers = (0..buffer_count) .map(|_| { // SAFETY: the invariants of `Layout` are respected. unsafe { std::alloc::alloc(mem_layout) } }) .collect(); Self { buffers, mem_layout, layout } } } #[derive(Debug)] pub struct DmabufFrame { pub fds: Vec, pub layout: FrameLayout, } impl Drop for UserPtrFrame { fn drop(&mut self) { for buffer in std::mem::take(&mut self.buffers).into_iter() { // Safe because we allocated the memory using `std::alloc::alloc`. unsafe { std::alloc::dealloc(buffer, self.mem_layout) } } } }