//! The decoder the real-time tests hold an encoder's stream to: libdav1d (BSD-2-Clause), //! loaded at run time where the host has it. Nothing here is linked or built: the library //! is the host's own, `libdav1d.so.7`. //! //! The four structures it is driven through are laid out as its headers at 1.5.3 have //! them (API 7.0.0: `include/dav1d/dav1d.h`, `data.h`, `picture.h`); the offsets below //! were printed by a C program compiled against those headers, the same on x86-64 and //! aarch64 Linux. A library of another API major is not used. use std::ffi::{c_char, c_int, c_void, CStr, CString}; #[cfg_attr(target_os = "linux", link(name = "dl"))] unsafe extern "C" { fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void; fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; } /// `Dav1dSettings` is 96 octets, `Dav1dData` 72 and `Dav1dPicture` 272: each is given more. #[repr(C, align(16))] struct Buf([u8; 1024]); const SETTINGS_N_THREADS: usize = 0; const SETTINGS_MAX_FRAME_DELAY: usize = 4; const SETTINGS_STRICT_STD_COMPLIANCE: usize = 64; const DATA_SZ: usize = 8; const PICTURE_DATA: usize = 16; const PICTURE_STRIDE: usize = 40; const PICTURE_W: usize = 56; const PICTURE_H: usize = 60; const PICTURE_LAYOUT: usize = 64; const PICTURE_BPC: usize = 68; /// `DAV1D_PIXEL_LAYOUT_I420`. pub const LAYOUT_I420: i32 = 1; #[cfg(target_os = "linux")] const EAGAIN: c_int = 11; #[cfg(not(target_os = "linux"))] const EAGAIN: c_int = 35; /// One decoded picture: its size, depth and layout, and its luma plane. pub struct Picture { pub w: u32, pub h: u32, pub bpc: u32, pub layout: i32, pub luma: Vec, } pub struct Dav1d { ctx: *mut c_void, pub version: String, data_create: unsafe extern "C" fn(*mut Buf, usize) -> *mut u8, send_data: unsafe extern "C" fn(*mut c_void, *mut Buf) -> c_int, get_picture: unsafe extern "C" fn(*mut c_void, *mut Buf) -> c_int, picture_unref: unsafe extern "C" fn(*mut Buf), close: unsafe extern "C" fn(*mut *mut c_void), } unsafe fn sym(lib: *mut c_void, name: &str) -> Result { let c = CString::new(name).unwrap(); // SAFETY: dlsym on a live handle with a valid C string. let p = unsafe { dlsym(lib, c.as_ptr()) }; if p.is_null() { return Err(format!("libdav1d has no {name}")); } // SAFETY: a function pointer the caller names the signature of. Ok(unsafe { std::mem::transmute_copy(&p) }) } impl Dav1d { /// The host's libdav1d, strict about the standard, one frame at a time; or why not. pub fn open() -> Result { let mut lib = std::ptr::null_mut(); for name in ["libdav1d.so.7", "libdav1d.7.dylib", "/opt/homebrew/lib/libdav1d.7.dylib"] { let c = CString::new(name).unwrap(); // SAFETY: dlopen with a valid C string; a failure returns null. lib = unsafe { dlopen(c.as_ptr(), 2) }; if !lib.is_null() { break; } } if lib.is_null() { return Err("no libdav1d of API 7 on this host (libdav1d.so.7)".into()); } // SAFETY: each symbol has the signature `dav1d.h` declares at API 7. unsafe { let version: unsafe extern "C" fn() -> *const c_char = sym(lib, "dav1d_version")?; let api: unsafe extern "C" fn() -> u32 = sym(lib, "dav1d_version_api")?; let default_settings: unsafe extern "C" fn(*mut Buf) = sym(lib, "dav1d_default_settings")?; let open: unsafe extern "C" fn(*mut *mut c_void, *const Buf) -> c_int = sym(lib, "dav1d_open")?; let v = CStr::from_ptr(version()).to_string_lossy().into_owned(); let a = api(); if a >> 16 != 7 { return Err(format!("libdav1d {v} is of API {}.{}.{}, and the layouts here are API 7's", a >> 16, (a >> 8) & 255, a & 255)); } let mut s = Box::new(Buf([0; 1024])); default_settings(&mut *s); s.0[SETTINGS_N_THREADS..SETTINGS_N_THREADS + 4].copy_from_slice(&1i32.to_ne_bytes()); s.0[SETTINGS_MAX_FRAME_DELAY..SETTINGS_MAX_FRAME_DELAY + 4].copy_from_slice(&1i32.to_ne_bytes()); s.0[SETTINGS_STRICT_STD_COMPLIANCE..SETTINGS_STRICT_STD_COMPLIANCE + 4].copy_from_slice(&1i32.to_ne_bytes()); let mut ctx = std::ptr::null_mut(); let r = open(&mut ctx, &*s); if r != 0 || ctx.is_null() { return Err(format!("dav1d_open: {r}")); } Ok(Dav1d { ctx, version: v, data_create: sym(lib, "dav1d_data_create")?, send_data: sym(lib, "dav1d_send_data")?, get_picture: sym(lib, "dav1d_get_picture")?, picture_unref: sym(lib, "dav1d_picture_unref")?, close: sym(lib, "dav1d_close")? }) } } /// Decode one temporal unit (its OBUs, with or without a temporal delimiter): the /// pictures it shows, or the decoder's refusal. pub fn decode(&mut self, unit: &[u8]) -> Result, String> { let mut out = Vec::new(); let mut data = Box::new(Buf([0; 1024])); // SAFETY: the library allocates `unit.len()` octets and describes them in `data`. let p = unsafe { (self.data_create)(&mut *data, unit.len()) }; if p.is_null() { return Err("dav1d_data_create".into()); } // SAFETY: a buffer of `unit.len()` octets the library just allocated. unsafe { std::ptr::copy_nonoverlapping(unit.as_ptr(), p, unit.len()) }; loop { // SAFETY: the context and the data it is to take; it takes what it can. let r = unsafe { (self.send_data)(self.ctx, &mut *data) }; if r != 0 && r != -EAGAIN { return Err(format!("dav1d_send_data: {r}")); } loop { let mut pic = Box::new(Buf([0; 1024])); // SAFETY: a zeroed picture for the library to fill. let g = unsafe { (self.get_picture)(self.ctx, &mut *pic) }; if g == -EAGAIN { break; } if g != 0 { return Err(format!("dav1d_get_picture: {g}")); } let i32_at = |o: usize| i32::from_ne_bytes(pic.0[o..o + 4].try_into().unwrap()); let usize_at = |o: usize| usize::from_ne_bytes(pic.0[o..o + 8].try_into().unwrap()); let (w, h, bpc) = (i32_at(PICTURE_W) as usize, i32_at(PICTURE_H) as usize, i32_at(PICTURE_BPC) as u32); let (base, stride) = (usize_at(PICTURE_DATA) as *const u8, usize_at(PICTURE_STRIDE)); let mut luma = Vec::with_capacity(w * h); for y in 0..h { // SAFETY: row `y` of the luma plane the picture holds until it is unreferenced: // `w` samples, of one octet at 8 bits and two above. unsafe { let row = base.add(y * stride); if bpc == 8 { luma.extend(std::slice::from_raw_parts(row, w).iter().map(|v| *v as u16)); } else { luma.extend(std::slice::from_raw_parts(row, 2 * w).as_chunks::<2>().0.iter().map(|b| u16::from_ne_bytes(*b))); } } } out.push(Picture { w: w as u32, h: h as u32, bpc, layout: i32_at(PICTURE_LAYOUT), luma }); // SAFETY: the picture just read, unreferenced once. unsafe { (self.picture_unref)(&mut *pic) }; } if usize::from_ne_bytes(data.0[DATA_SZ..DATA_SZ + 8].try_into().unwrap()) == 0 { return Ok(out); } } } } impl Drop for Dav1d { fn drop(&mut self) { // SAFETY: the context this opened, closed once. unsafe { (self.close)(&mut self.ctx) }; } }