#![allow(
unsafe_code,
reason = "rav1d's only Rust API is unsafe extern \"C\" functions; this module wraps them safely"
)]
use std::ptr::NonNull;
use std::slice;
use rav1d::include::dav1d::data::Dav1dData;
use rav1d::include::dav1d::dav1d::Dav1dContext;
use rav1d::include::dav1d::dav1d::Dav1dSettings;
use rav1d::include::dav1d::headers::DAV1D_PIXEL_LAYOUT_I444;
use rav1d::include::dav1d::picture::Dav1dPicture;
use rav1d::src::lib::dav1d_close;
use rav1d::src::lib::dav1d_data_create;
use rav1d::src::lib::dav1d_default_settings;
use rav1d::src::lib::dav1d_get_picture;
use rav1d::src::lib::dav1d_open;
use rav1d::src::lib::dav1d_picture_unref;
use rav1d::src::lib::dav1d_send_data;
pub struct Decoder {
ctx: Option<Dav1dContext>,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum DecoderError {
Open(i32),
SendData(i32),
GetPicture(i32),
DataAllocFailed,
UnexpectedFormat { bpc: i32, layout: u32 },
}
impl std::fmt::Display for DecoderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Open(c) => write!(f, "dav1d_open failed: errno {c}"),
Self::SendData(c) => write!(f, "dav1d_send_data failed: errno {c}"),
Self::GetPicture(c) => write!(f, "dav1d_get_picture failed: errno {c}"),
Self::DataAllocFailed => f.write_str("dav1d_data_create returned null"),
Self::UnexpectedFormat { bpc, layout } => {
write!(
f,
"decoder produced unexpected pixel format (bpc={bpc}, layout={layout})"
)
}
}
}
}
impl std::error::Error for DecoderError {}
fn eagain_code() -> i32 {
-libc::EAGAIN
}
impl Decoder {
pub fn new() -> Result<Self, DecoderError> {
let mut settings: Dav1dSettings = unsafe { std::mem::zeroed() };
unsafe { dav1d_default_settings(NonNull::from(&mut settings)) };
let mut ctx: Option<Dav1dContext> = None;
let result = unsafe {
dav1d_open(
Some(NonNull::from(&mut ctx)),
Some(NonNull::from(&mut settings)),
)
};
if result.0 != 0 {
return Err(DecoderError::Open(result.0));
}
Ok(Self { ctx })
}
pub fn feed(&mut self, bytes: &[u8]) -> Result<bool, DecoderError> {
let mut data = Dav1dData::default();
let ptr = unsafe { dav1d_data_create(Some(NonNull::from(&mut data)), bytes.len()) };
if ptr.is_null() {
return Err(DecoderError::DataAllocFailed);
}
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
}
let result = unsafe { dav1d_send_data(self.ctx, Some(NonNull::from(&mut data))) };
match result.0 {
0 => Ok(true),
x if x == eagain_code() => Ok(false),
err => Err(DecoderError::SendData(err)),
}
}
pub fn next_picture(&mut self) -> Result<Option<DecodedPicture>, DecoderError> {
let mut pic = Dav1dPicture::default();
let result = unsafe { dav1d_get_picture(self.ctx, Some(NonNull::from(&mut pic))) };
match result.0 {
0 => Ok(Some(DecodedPicture { pic })),
x if x == eagain_code() => Ok(None),
err => Err(DecoderError::GetPicture(err)),
}
}
}
impl Drop for Decoder {
fn drop(&mut self) {
if self.ctx.is_some() {
unsafe { dav1d_close(Some(NonNull::from(&mut self.ctx))) };
}
}
}
pub struct DecodedPicture {
pic: Dav1dPicture,
}
impl DecodedPicture {
pub fn width(&self) -> usize {
self.pic.p.w as usize
}
pub fn height(&self) -> usize {
self.pic.p.h as usize
}
pub fn check_format(&self) -> Result<(), DecoderError> {
if self.pic.p.bpc == 10 && self.pic.p.layout == DAV1D_PIXEL_LAYOUT_I444 {
Ok(())
} else {
Err(DecoderError::UnexpectedFormat {
bpc: self.pic.p.bpc,
layout: self.pic.p.layout,
})
}
}
pub fn plane_row(&self, plane: usize, row: usize) -> &[u16] {
debug_assert!(plane < 3, "plane index out of range");
assert!(row < self.height(), "row {row} >= height {}", self.height());
let stride_bytes = if plane == 0 {
self.pic.stride[0] as usize
} else {
self.pic.stride[1] as usize
};
let ptr = self.pic.data[plane]
.expect("decoded plane pointer is null")
.as_ptr()
.cast::<u8>();
let row_ptr = unsafe { ptr.add(row * stride_bytes).cast::<u16>() };
unsafe { slice::from_raw_parts(row_ptr, self.width()) }
}
}
impl Drop for DecodedPicture {
fn drop(&mut self) {
unsafe { dav1d_picture_unref(Some(NonNull::from(&mut self.pic))) };
}
}