blob: c6f3f02e308e7cce9483686313b1a6ca6dbd92e0 [file] [log] [blame]
// 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<T>(x: T, alignment: T) -> T
where
T: Add<Output = T> + Sub<Output = T> + Div<Output = T> + Mul<Output = T> + From<u8> + 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::<std::collections::HashSet<usize>>()
.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<OwnedFd>,
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) }
}
}
}