Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy dav1d as the Rust API #1362

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

emilk
Copy link
Contributor

@emilk emilk commented Oct 3, 2024

This basically copy-pastes https://github.com/rust-av/dav1d-rs/blob/master/src/lib.rs (actually via https://github.com/rerun-io/rav1d-rs/blob/master/src/lib.rs) into rav1d to give rav1d a proper Rust interface.

It is a bit silly to wrap the C FFI, but it works.

The upshot is that this makes rav1d a drop-in replacement for https://crates.io/crates/dav1d

@emilk emilk marked this pull request as ready for review October 4, 2024 01:39
kkysen added a commit that referenced this pull request Oct 4, 2024
Found when implementing a Rust API in #1362.
@emilk emilk changed the title Copy dav1d as the Rust API Copy dav1d as the Rust API Oct 7, 2024
@kkysen kkysen self-assigned this Oct 8, 2024
Copy link
Collaborator

@kkysen kkysen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a public Rust API matching dav1d-rs seems like it'd be very nice, as that Rusty API has already been designed for us evidently. However, this is way too much unsafe code that is entirely undocumented/unjustified, and I don't really see why we can't wrap the safe but private Rust API that we have right now (the rav1d_* versions of all of the dav1d_* functions, which are all safe, but otherwise have very similar APIs besides converting the data types).

bitflags = "2.4.0"
cfg-if = "1.0.0"
libc = "0.2"
parking_lot = "0.12.2"
paste = "1.0.14"
raw-cpuid = "11.0.1"
static_assertions = "1"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep this like the other dependencies with a full major.minor.patch? We just used what cargo add gave us.

Comment on lines +196 to +206
impl From<Rav1dError> for Error {
fn from(err: Rav1dError) -> Self {
match err {
Rav1dError::EAGAIN => Error::Again,
Rav1dError::ENOMEM => Error::NotEnoughMemory,
Rav1dError::EINVAL => Error::InvalidArgument,
Rav1dError::ENOPROTOOPT => Error::UnsupportedBitstream,
_ => Error::UnknownError(err),
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we just want to use this as Rav1dError in the first place?

Comment on lines +246 to +254
unsafe {
let mut dav1d_settings = mem::MaybeUninit::uninit();

dav1d_default_settings(NonNull::new(dav1d_settings.as_mut_ptr()).unwrap());

Self {
dav1d_settings: dav1d_settings.assume_init(),
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is pretty trivial to do safely using the Rust API.

Comment on lines +339 to +346
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct InloopFilterType: u32 {
const DEBLOCK = DAV1D_INLOOPFILTER_DEBLOCK;
const CDEF = DAV1D_INLOOPFILTER_CDEF;
const RESTORATION = DAV1D_INLOOPFILTER_RESTORATION;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd prefer just making Rav1dInloopFilterType this instead of doing a conversion here at the edge of the API.

Comment on lines +357 to +369
impl TryFrom<u32> for DecodeFrameType {
type Error = TryFromEnumError;

fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
DAV1D_DECODEFRAMETYPE_ALL => Ok(DecodeFrameType::All),
DAV1D_DECODEFRAMETYPE_REFERENCE => Ok(DecodeFrameType::Reference),
DAV1D_DECODEFRAMETYPE_INTRA => Ok(DecodeFrameType::Intra),
DAV1D_DECODEFRAMETYPE_KEY => Ok(DecodeFrameType::Key),
_ => Err(TryFromEnumError(())),
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TryFromEnumError(());

impl std::fmt::Display for TryFromEnumError {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've preferred fully importing these kinds of items (e.x. use std::fmt::Display).

cookie: Option<SendSyncNonNull<std::ffi::c_void>>,
) {
let cookie = cookie.unwrap().as_ptr().as_ptr();
let buf = unsafe { Box::from_raw(cookie as *mut T) };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't enabled it in CI yet, but we have #![deny(clippy::undocumented_unsafe_blocks)] on. All unsafe blocks should be documented with // SAFETY comments.

Comment on lines +616 to +638
/// Pixel layout of a frame.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum PixelLayout {
/// Monochrome.
I400,
/// 4:2:0 planar.
I420,
/// 4:2:2 planar.
I422,
/// 4:4:4 planar.
I444,
}

/// Frame component.
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum PlanarImageComponent {
/// Y component.
Y,
/// U component.
U,
/// V component.
V,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are much better names than we came up with (we don't know much about AV1 or video codecs themselves). We should try to just use these internally more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide Rust API
3 participants