#![deny(unsafe_op_in_unsafe_fn)] use crate::include::common::bitdepth::BitDepth; use crate::include::common::validate::validate_input; use crate::include::dav1d::common::Dav1dDataProps; use crate::include::dav1d::common::Rav1dDataProps; use crate::include::dav1d::headers::DRav1d; use crate::include::dav1d::headers::Dav1dFrameHeader; use crate::include::dav1d::headers::Dav1dITUTT35; use crate::include::dav1d::headers::Dav1dPixelLayout; use crate::include::dav1d::headers::Dav1dSequenceHeader; use crate::include::dav1d::headers::Rav1dContentLightLevel; use crate::include::dav1d::headers::Rav1dFrameHeader; use crate::include::dav1d::headers::Rav1dITUTT35; use crate::include::dav1d::headers::Rav1dMasteringDisplay; use crate::include::dav1d::headers::Rav1dPixelLayout; use crate::include::dav1d::headers::Rav1dSequenceHeader; use crate::src::assume::assume; use crate::src::c_arc::RawArc; use crate::src::disjoint_mut::AsMutPtr; use crate::src::disjoint_mut::DisjointImmutGuard; use crate::src::disjoint_mut::DisjointMut; use crate::src::disjoint_mut::DisjointMutGuard; use crate::src::disjoint_mut::SliceBounds; use crate::src::error::Dav1dResult; use crate::src::error::Rav1dError; use crate::src::error::Rav1dError::EINVAL; use crate::src::error::Rav1dResult; use crate::src::pixels::Pixels; use crate::src::send_sync_non_null::SendSyncNonNull; use crate::src::strided::Strided; use crate::src::with_offset::WithOffset; use libc::ptrdiff_t; use libc::uintptr_t; use std::array; use std::ffi::c_int; use std::ffi::c_void; use std::mem; use std::ptr::NonNull; use std::sync::Arc; use to_method::To as _; use zerocopy::AsBytes; use zerocopy::FromBytes; use zerocopy::FromZeroes; pub(crate) const RAV1D_PICTURE_ALIGNMENT: usize = 64; pub const DAV1D_PICTURE_ALIGNMENT: usize = RAV1D_PICTURE_ALIGNMENT; #[derive(Default)] #[repr(C)] pub struct Dav1dPictureParameters { pub w: c_int, pub h: c_int, pub layout: Dav1dPixelLayout, pub bpc: c_int, } // TODO(kkysen) Eventually the [`impl Default`] might not be needed. #[derive(Clone, Default)] #[repr(C)] pub(crate) struct Rav1dPictureParameters { pub w: c_int, pub h: c_int, pub layout: Rav1dPixelLayout, pub bpc: u8, } impl From for Rav1dPictureParameters { fn from(value: Dav1dPictureParameters) -> Self { let Dav1dPictureParameters { w, h, layout, bpc } = value; Self { w, h, layout: layout.try_into().unwrap(), bpc: bpc.try_into().unwrap(), } } } impl From for Dav1dPictureParameters { fn from(value: Rav1dPictureParameters) -> Self { let Rav1dPictureParameters { w, h, layout, bpc } = value; Self { w, h, layout: layout.into(), bpc: bpc.into(), } } } #[derive(Default)] #[repr(C)] pub struct Dav1dPicture { pub seq_hdr: Option>, pub frame_hdr: Option>, pub data: [Option>; 3], pub stride: [ptrdiff_t; 2], pub p: Dav1dPictureParameters, pub m: Dav1dDataProps, pub content_light: Option>, pub mastering_display: Option>, pub itut_t35: Option>, pub n_itut_t35: usize, pub reserved: [uintptr_t; 4], pub frame_hdr_ref: Option>>, // opaque, so we can change this pub seq_hdr_ref: Option>>, // opaque, so we can change this pub content_light_ref: Option>, // opaque, so we can change this pub mastering_display_ref: Option>, // opaque, so we can change this pub itut_t35_ref: Option, Box<[Dav1dITUTT35]>>>>, // opaque, so we can change this pub reserved_ref: [uintptr_t; 4], pub r#ref: Option>, // opaque, so we can change this pub allocator_data: Option>, } #[derive(Clone, FromZeroes, FromBytes, AsBytes)] #[repr(C, align(64))] pub struct AlignedPixelChunk([u8; RAV1D_PICTURE_ALIGNMENT]); const _: () = assert!(mem::align_of::() == RAV1D_PICTURE_ALIGNMENT); const _: () = assert!(mem::size_of::() == RAV1D_PICTURE_ALIGNMENT); /// The guaranteed length multiple of [`Rav1dPictureDataComponentInner`]s. /// This is checked and [`assume`]d. const RAV1D_PICTURE_GUARANTEED_MULTIPLE: usize = 64; /// Actual [`Rav1dPictureData`]'s components should be multiples of this, /// as this is guaranteed by [`Rav1dPicAllocator::alloc_picture_callback`], /// though wrapped buffers may only be [`RAV1D_PICTURE_GUARANTEED_MULTIPLE`]. const RAV1D_PICTURE_MULTIPLE: usize = 64 * 64; pub struct Rav1dPictureDataComponentInner { /// A ptr to the start of this slice of [`BitDepth::Pixel`]s*, /// even if [`Self::stride`] is negative. /// /// It is aligned to [`RAV1D_PICTURE_ALIGNMENT`]. ptr: NonNull, /// The length of [`Self::ptr`] in [`u8`] bytes. /// /// It is a multiple of [`RAV1D_PICTURE_GUARANTEED_MULTIPLE`]. len: usize, /// The stride of [`Self::ptr`] in [`u8`] bytes. stride: isize, } impl Rav1dPictureDataComponentInner { /// `len` and `stride` are in terms of [`u8`] bytes. /// /// # Safety /// /// `ptr`, `len`, and `stride` must follow the requirements of [`Dav1dPicAllocator::alloc_picture_callback`]. unsafe fn new(ptr: Option>, len: usize, stride: isize) -> Self { let ptr = match ptr { None => { return Self { // Ensure it is aligned enough. ptr: NonNull::::dangling().cast(), len: 0, stride, }; } Some(ptr) => ptr, }; assert!(len != 0); // If `len` was 0, `ptr` should've been `None`. assert!(ptr.cast::().is_aligned()); let ptr = if stride < 0 { let ptr = ptr.as_ptr(); // SAFETY: According to `Dav1dPicAllocator::alloc_picture_callback`, // if the `stride` is negative, this is how we get the start of the data. // `.offset(-stride)` puts us at one element past the end of the slice, // and `.sub(len)` puts us back at the start of the slice. let ptr = unsafe { ptr.offset(-stride).sub(len) }; NonNull::new(ptr).unwrap() } else { ptr }; // Guaranteed by `Dav1dPicAllocator::alloc_picture_callback`. assert!(len % RAV1D_PICTURE_MULTIPLE == 0); Self { ptr, len, stride } } /// # Safety /// /// As opposed to [`Self::new`], this is safe because `buf` is a `&mut` and thus unique, /// so it is sound to further subdivide it into disjoint `&mut`s. pub fn wrap_buf(buf: &mut [BD::Pixel], stride: usize) -> Self { let buf = AsBytes::as_bytes_mut(buf); let ptr = NonNull::new(buf.as_mut_ptr()).unwrap(); assert!(ptr.cast::().is_aligned()); let len = buf.len(); assert!(len % RAV1D_PICTURE_GUARANTEED_MULTIPLE == 0); let stride = (stride * mem::size_of::()) as isize; Self { ptr, len, stride } } } // SAFETY: We only store the raw pointer, so we never materialize a `&mut`. unsafe impl AsMutPtr for Rav1dPictureDataComponentInner { type Target = u8; #[inline] // Inline so callers can see the assume. unsafe fn as_mut_ptr(ptr: *mut Self) -> *mut Self::Target { // SAFETY: Safe to dereference by unsafe preconditions. // Since we don't store any `&mut`s, just a raw ptr, we can have a `&Self`. let this = unsafe { &*ptr }; // Assume this so that the compiler knows `ptr` is aligned. // Normally we'd store this as a slice so the compiler would know, // but since it's a ptr due to `DisjointMut`, we explicitly assume it here. // SAFETY: We already checked this in `Self::new`. unsafe { assume(this.ptr.cast::().is_aligned()) }; this.ptr.as_ptr() } #[inline] // Inline so callers can see the assume. fn len(&self) -> usize { // SAFETY: We already checked this in `Self::new`. unsafe { assume(self.len % RAV1D_PICTURE_GUARANTEED_MULTIPLE == 0) }; self.len } } pub struct Rav1dPictureDataComponent(DisjointMut); impl Pixels for Rav1dPictureDataComponent { fn byte_len(&self) -> usize { self.0.len() } fn as_byte_mut_ptr(&self) -> *mut u8 { self.0.as_mut_ptr() } } impl Strided for Rav1dPictureDataComponent { fn stride(&self) -> isize { // SAFETY: We're only accessing the `stride` fields, not `ptr`. unsafe { (*self.0.inner()).stride } } } impl Rav1dPictureDataComponent { pub fn wrap_buf(buf: &mut [BD::Pixel], stride: usize) -> Self { Self(DisjointMut::new( Rav1dPictureDataComponentInner::wrap_buf::(buf, stride), )) } pub fn pixel_offset(&self) -> usize { let stride = self.stride(); if stride >= 0 { return 0; } BD::pxstride(self.byte_len() - (-stride) as usize) } pub fn with_offset(&self) -> Rav1dPictureDataComponentOffset { Rav1dPictureDataComponentOffset { data: self, offset: self.pixel_offset::(), } } /// Strided ptr to [`u8`] bytes. fn as_strided_byte_mut_ptr(&self) -> *mut u8 { let ptr = self.0.as_mut_ptr(); let stride = self.stride(); if stride < 0 { // SAFETY: This puts `ptr` one element past the end of the slice of pixels. let ptr = unsafe { ptr.add(self.byte_len()) }; // SAFETY: `stride` is negative and `-stride < len`, so this should stay in bounds. let ptr = unsafe { ptr.offset(stride) }; ptr } else { ptr } } /// Strided ptr to [`BitDepth::Pixel`]s. pub fn as_strided_mut_ptr(&self) -> *mut BD::Pixel { // SAFETY: Transmutation is safe because we verify this with `zerocopy` in `Self::slice`. self.as_strided_byte_mut_ptr().cast() } /// Strided ptr to [`BitDepth::Pixel`]s. pub fn as_strided_ptr(&self) -> *const BD::Pixel { self.as_strided_mut_ptr::().cast_const() } fn as_dav1d(&self) -> Option> { if self.byte_len() == 0 { None } else { NonNull::new(self.as_strided_byte_mut_ptr().cast()) } } pub fn copy_from(&self, src: &Self) { let dst = &mut *self.0.index_mut(..); let src = &*src.0.index(..); dst.clone_from_slice(src); } #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn index<'a, BD: BitDepth>( &'a self, index: usize, ) -> DisjointImmutGuard<'a, Rav1dPictureDataComponentInner, BD::Pixel> { self.0.element_as(index) } #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn index_mut<'a, BD: BitDepth>( &'a self, index: usize, ) -> DisjointMutGuard<'a, Rav1dPictureDataComponentInner, BD::Pixel> { self.0.mut_element_as(index) } #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn slice<'a, BD, I>( &'a self, index: I, ) -> DisjointImmutGuard<'a, Rav1dPictureDataComponentInner, [BD::Pixel]> where BD: BitDepth, I: SliceBounds, { self.0.slice_as(index) } #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn slice_mut<'a, BD, I>( &'a self, index: I, ) -> DisjointMutGuard<'a, Rav1dPictureDataComponentInner, [BD::Pixel]> where BD: BitDepth, I: SliceBounds, { self.0.mut_slice_as(index) } } pub type Rav1dPictureDataComponentOffset<'a> = WithOffset<&'a Rav1dPictureDataComponent>; impl<'a> Rav1dPictureDataComponentOffset<'a> { #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn index( &self, ) -> DisjointImmutGuard<'a, Rav1dPictureDataComponentInner, BD::Pixel> { self.data.index::(self.offset) } #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn index_mut( &self, ) -> DisjointMutGuard<'a, Rav1dPictureDataComponentInner, BD::Pixel> { self.data.index_mut::(self.offset) } #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn slice( &self, len: usize, ) -> DisjointImmutGuard<'a, Rav1dPictureDataComponentInner, [BD::Pixel]> { self.data.slice::((self.offset.., ..len)) } #[inline] // Inline to see bounds checks in order to potentially elide them. #[cfg_attr(debug_assertions, track_caller)] pub fn slice_mut( &self, len: usize, ) -> DisjointMutGuard<'a, Rav1dPictureDataComponentInner, [BD::Pixel]> { self.data.slice_mut::((self.offset.., ..len)) } } pub struct Rav1dPictureData { pub data: [Rav1dPictureDataComponent; 3], pub(crate) allocator_data: Option>, pub(crate) allocator: Rav1dPicAllocator, } impl Drop for Rav1dPictureData { fn drop(&mut self) { let Self { data, allocator_data, allocator, } = self; allocator.dealloc_picture_data(data, *allocator_data); } } // TODO(kkysen) Eventually the [`impl Default`] might not be needed. // It's needed currently for a [`mem::take`] that simulates a move, // but once everything is Rusty, we may not need to clear the `dst` anymore. // This also applies to the `#[derive(Default)]` // on [`Rav1dPictureParameters`] and [`Rav1dPixelLayout`]. #[derive(Clone, Default)] #[repr(C)] pub(crate) struct Rav1dPicture { pub seq_hdr: Option>>, pub frame_hdr: Option>>, pub data: Option>, pub stride: [ptrdiff_t; 2], pub p: Rav1dPictureParameters, pub m: Rav1dDataProps, pub content_light: Option>, pub mastering_display: Option>, pub itut_t35: Arc, Box<[Dav1dITUTT35]>>>, } impl From for Rav1dPicture { fn from(value: Dav1dPicture) -> Self { let Dav1dPicture { seq_hdr: _, frame_hdr: _, data: _, stride, p, m, content_light: _, mastering_display: _, itut_t35: _, n_itut_t35: _, reserved: _, frame_hdr_ref, seq_hdr_ref, content_light_ref, mastering_display_ref, itut_t35_ref, reserved_ref: _, r#ref: data_ref, allocator_data: _, } = value; Self { // We don't `.update_rav1d()` [`Rav1dSequenceHeader`] because it's meant to be read-only. seq_hdr: seq_hdr_ref.map(|raw| { // SAFETY: `raw` came from [`RawArc::from_arc`]. unsafe { raw.into_arc() } }), // We don't `.update_rav1d()` [`Rav1dFrameHeader`] because it's meant to be read-only. frame_hdr: frame_hdr_ref.map(|raw| { // SAFETY: `raw` came from [`RawArc::from_arc`]. unsafe { raw.into_arc() } }), data: data_ref.map(|raw| { // SAFETY: `raw` came from [`RawArc::from_arc`]. unsafe { raw.into_arc() } }), stride, p: p.into(), m: m.into(), content_light: content_light_ref.map(|raw| { // SAFETY: `raw` came from [`RawArc::from_arc`]. unsafe { raw.into_arc() } }), mastering_display: mastering_display_ref.map(|raw| { // Safety: `raw` came from [`RawArc::from_arc`]. unsafe { raw.into_arc() } }), // We don't `.update_rav1d` [`Rav1dITUTT35`] because never read it. itut_t35: itut_t35_ref .map(|raw| { // SAFETY: `raw` came from [`RawArc::from_arc`]. unsafe { raw.into_arc() } }) .unwrap_or_default(), } } } impl From for Dav1dPicture { fn from(value: Rav1dPicture) -> Self { let Rav1dPicture { seq_hdr, frame_hdr, data, stride, p, m, content_light, mastering_display, itut_t35, } = value; Self { // [`DRav1d::from_rav1d`] is called right after [`parse_seq_hdr`]. seq_hdr: seq_hdr.as_ref().map(|arc| (&arc.as_ref().dav1d).into()), // [`DRav1d::from_rav1d`] is called in [`parse_frame_hdr`]. frame_hdr: frame_hdr.as_ref().map(|arc| (&arc.as_ref().dav1d).into()), data: data .as_ref() .map(|arc| arc.data.each_ref().map(|data| data.as_dav1d())) .unwrap_or_default(), stride, p: p.into(), m: m.into(), content_light: content_light.as_ref().map(|arc| arc.as_ref().into()), mastering_display: mastering_display.as_ref().map(|arc| arc.as_ref().into()), // [`DRav1d::from_rav1d`] is called in [`rav1d_parse_obus`]. itut_t35: Some(NonNull::new(itut_t35.dav1d.as_ptr().cast_mut()).unwrap()), n_itut_t35: itut_t35.len(), reserved: Default::default(), frame_hdr_ref: frame_hdr.map(RawArc::from_arc), seq_hdr_ref: seq_hdr.map(RawArc::from_arc), content_light_ref: content_light.map(RawArc::from_arc), mastering_display_ref: mastering_display.map(RawArc::from_arc), itut_t35_ref: Some(itut_t35).map(RawArc::from_arc), reserved_ref: Default::default(), // Order flipped so that the borrow comes before the move. allocator_data: data.as_ref().and_then(|arc| arc.allocator_data), r#ref: data.map(RawArc::from_arc), } } } impl Rav1dPicture { pub fn lf_offsets(&self, y: c_int) -> [Rav1dPictureDataComponentOffset; 3] { // Init loopfilter offsets. Point the chroma offsets in 4:0:0 to the luma // plane here to avoid having additional in-loop branches in various places. // We never use those values, so it doesn't really matter what they point // at, as long as the offsets are valid. let layout = self.p.layout; let has_chroma = layout != Rav1dPixelLayout::I400; let data = &self.data.as_ref().unwrap().data; array::from_fn(|i| { let data = &data[has_chroma as usize * i]; let ss_ver = layout == Rav1dPixelLayout::I420 && i != 0; data.with_offset::() + (y as isize * data.pixel_stride::() >> ss_ver as u8) }) } } #[derive(Clone)] #[repr(C)] pub struct Dav1dPicAllocator { /// Custom data to pass to the allocator callbacks. /// /// # Safety /// /// All accesses to [`Self::cookie`] must be thread-safe /// (i.e. [`Self::cookie`] must be [`Send`]` + `[`Sync`]). /// /// If used from Rust, [`Self::cookie`] is a [`SendSyncNonNull`], /// whose constructors ensure this [`Send`]` + `[`Sync`] safety. pub cookie: Option>, /// Allocate the picture buffer based on the [`Dav1dPictureParameters`]. /// /// [`data`]`[0]`, [`data`]`[1]` and [`data`]`[2]` /// must be [`DAV1D_PICTURE_ALIGNMENT`]-byte aligned /// and with a pixel width/height multiple of 128 pixels. /// Any allocated memory area should also be padded by [`DAV1D_PICTURE_ALIGNMENT`] bytes. /// [`data`]`[1]` and [`data`]`[2]` must share the same [`stride`]`[1]`. /// /// # Safety /// /// See [`Self::cookie`]'s safety requirements. /// /// ### Additional `rav1d` requirement: /// /// The allocated data must be initialized. /// If newly (e.x. not reused) allocated data is zero initialized using OS APIs, /// it is possible for this to not be slower than an uninitialized allocation. /// For example, see `dav1d_default_picture_alloc` and `MemPool::pop_init`. /// /// If the allocated data is not initialized, /// it is possible there will be reads of uninitialized data. /// `rav1d` should not read this data before writing to it first, /// but it does not guarantee that it does so. /// Instead, initializing the allocated data guarantees all uses of it will be sound. /// /// # Args /// /// * `pic`: The picture to allocate the buffer for. /// The callback needs to fill the picture /// [`data`]`[0]`, [`data`]`[1]`, [`data`]`[2]`, /// [`stride`]`[0]`, and [`stride`]`[1]`. /// The allocator can fill the pic [`allocator_data`] pointer /// with a custom pointer that will be passed to /// [`release_picture_callback`]. /// /// The only fields of `pic` that will be already set are: /// * [`Dav1dPicture::p`] /// * [`Dav1dPicture::seq_hdr`] /// * [`Dav1dPicture::frame_hdr`] /// /// This is not a change from the original `DAV1D_API`, /// just a clarification of it. /// /// * `cookie`: Custom pointer passed to all calls. /// /// *Note*: No fields other than [`data`], [`stride`] and [`allocator_data`] /// must be filled by this callback. /// /// # Return /// /// 0 on success. A negative `DAV1D_ERR` value on error. /// /// /// [`data`]: Dav1dPicture::data /// [`stride`]: Dav1dPicture::data /// [`allocator_data`]: Dav1dPicture::allocator_data /// [`release_picture_callback`]: Self::release_picture_callback pub alloc_picture_callback: Option< unsafe extern "C" fn( pic: *mut Dav1dPicture, cookie: Option>, ) -> Dav1dResult, >, /// Release the picture buffer. /// /// # Safety /// /// If frame threading is used, accesses to `cookie` must be thread-safe. /// /// If frame threading is used, this function may be called by the main thread /// (the thread which calls [`dav1d_get_picture`]), /// or any of the frame threads and thus must be thread-safe. /// If frame threading is not used, this function will only be called on the main thread. /// /// # Args /// /// * `pic`: The picture that was filled by [`alloc_picture_callback`]. /// /// The only fields of `pic` that will be set are /// the ones allocated by [`Self::alloc_picture_callback`]: /// * [`Dav1dPicture::data`] /// * [`Dav1dPicture::allocator_data`] /// /// NOTE: This is a slight change from the original `DAV1D_API`, which was underspecified. /// However, all known uses of this API follow this already: /// * `libdav1d`: [`dav1d_default_picture_release`](https://code.videolan.org/videolan/dav1d/-/blob/16ed8e8b99f2fcfffe016e929d3626e15267ad3e/src/picture.c#L85-87) /// * `dav1d`: [`picture_release`](https://code.videolan.org/videolan/dav1d/-/blob/16ed8e8b99f2fcfffe016e929d3626e15267ad3e/tools/dav1d.c#L180-182) /// * `dav1dplay`: [`placebo_release_pic`](https://code.videolan.org/videolan/dav1d/-/blob/16ed8e8b99f2fcfffe016e929d3626e15267ad3e/examples/dp_renderer_placebo.c#L375-383) /// * `libplacebo`: [`pl_release_dav1dpicture`](https://github.com/haasn/libplacebo/blob/34e019bfedaa5a64f268d8f9263db352c0a8f67f/src/include/libplacebo/utils/dav1d_internal.h#L594-L607) /// * `ffmpeg`: [`libdav1d_picture_release`](https://github.com/FFmpeg/FFmpeg/blob/00b288da73f45acb78b74bcc40f73c7ba1fff7cb/libavcodec/libdav1d.c#L124-L129) /// /// Making this API safe without this slight tightening of the API /// [is very difficult](https://github.com/memorysafety/rav1d/pull/685#discussion_r1458171639). /// /// * `cookie`: Custom pointer passed to all calls. /// /// [`dav1d_get_picture`]: crate::src::lib::dav1d_get_picture /// [`alloc_picture_callback`]: Self::alloc_picture_callback pub release_picture_callback: Option< unsafe extern "C" fn(pic: *mut Dav1dPicture, cookie: Option>) -> (), >, } #[derive(Clone)] #[repr(C)] pub(crate) struct Rav1dPicAllocator { /// See [`Dav1dPicAllocator::cookie`]. /// /// # Safety /// /// If [`Self::is_default`]`()`, then this cookie is a reference to /// [`Rav1dContext::picture_pool`], a `&Arc`. /// Thus, its lifetime is that of `&c.picture_pool`, /// so the lifetime of the `&`[`Rav1dContext`]. /// This is used from `dav1d_default_picture_alloc` /// ([`Self::default`]`().alloc_picture_callback`), /// which is called from [`Self::alloc_picture_data`], /// which is called further up on the call stack with a `&`[`Rav1dContext`]. /// Thus, the lifetime will always be valid where used. /// /// Note that this is an `&Arc` turned into a raw pointer, /// not an [`Arc::into_raw`] of that [`Arc`]. /// This is because storing the [`Arc`] would require C to /// free data owned by a [`Dav1dPicAllocator`] potentially, /// which it may not do, as there are no current APIs for doing so. /// /// [`Rav1dContext::picture_pool`]: crate::src::internal::Rav1dContext::picture_pool /// [`Rav1dContext`]: crate::src::internal::Rav1dContext pub cookie: Option>, /// See [`Dav1dPicAllocator::alloc_picture_callback`]. /// /// # Safety /// /// `pic` is passed as a `&mut`. /// /// If frame threading is used, accesses to [`Self::cookie`] must be thread-safe, /// i.e. [`Self::cookie`] must be [`Send`]` + `[`Sync`]. pub alloc_picture_callback: unsafe extern "C" fn( pic: *mut Dav1dPicture, cookie: Option>, ) -> Dav1dResult, /// See [`Dav1dPicAllocator::release_picture_callback`]. /// /// # Safety /// /// `pic` is passed as a `&mut`. /// /// If frame threading is used, accesses to [`Self::cookie`] must be thread-safe, /// i.e. [`Self::cookie`] must be [`Send`]` + `[`Sync`]. pub release_picture_callback: unsafe extern "C" fn(pic: *mut Dav1dPicture, cookie: Option>) -> (), } impl TryFrom for Rav1dPicAllocator { type Error = Rav1dError; fn try_from(value: Dav1dPicAllocator) -> Result { let Dav1dPicAllocator { cookie, alloc_picture_callback, release_picture_callback, } = value; Ok(Self { cookie, alloc_picture_callback: validate_input!(alloc_picture_callback.ok_or(EINVAL))?, release_picture_callback: validate_input!(release_picture_callback.ok_or(EINVAL))?, }) } } impl From for Dav1dPicAllocator { fn from(value: Rav1dPicAllocator) -> Self { let Rav1dPicAllocator { cookie, alloc_picture_callback, release_picture_callback, } = value; Self { cookie, alloc_picture_callback: Some(alloc_picture_callback), release_picture_callback: Some(release_picture_callback), } } } impl Rav1dPicAllocator { pub fn alloc_picture_data( &self, w: c_int, h: c_int, seq_hdr: Arc>, frame_hdr: Option>>, ) -> Rav1dResult { let pic = Rav1dPicture { p: Rav1dPictureParameters { w, h, layout: seq_hdr.layout, bpc: 8 + 2 * seq_hdr.hbd, }, seq_hdr: Some(seq_hdr), frame_hdr, ..Default::default() }; let mut pic_c = pic.to::(); // SAFETY: `pic_c` is a valid `Dav1dPicture` with `data`, `stride`, `allocator_data` unset. let result = unsafe { (self.alloc_picture_callback)(&mut pic_c, self.cookie) }; result.try_to::().unwrap()?; // `data`, `stride`, and `allocator_data` are the only fields set by the allocator. // Of those, only `data` and `allocator_data` are read through `r#ref`, // so we need to read those directly first and allocate the `Arc`. let data = pic_c.data; let allocator_data = pic_c.allocator_data; let mut pic = pic_c.to::(); let len = pic.p.pic_len(pic.stride); // TODO fallible allocation pic.data = Some(Arc::new(Rav1dPictureData { data: array::from_fn(|i| { let ptr = data[i].map(|ptr| ptr.cast::()); let len = len[(i != 0) as usize]; let stride = pic.stride[(i != 0) as usize]; // SAFETY: These args come from `Self::alloc_picture_callback`. let component = unsafe { Rav1dPictureDataComponentInner::new(ptr, len, stride) }; Rav1dPictureDataComponent(DisjointMut::new(component)) }), allocator_data, allocator: self.clone(), })); Ok(pic) } pub fn dealloc_picture_data( &self, data: &mut [Rav1dPictureDataComponent; 3], allocator_data: Option>, ) { let data = data.each_mut().map(|data| data.as_dav1d()); let mut pic_c = Dav1dPicture { data, allocator_data, ..Default::default() }; // SAFETY: `pic_c` contains the same `data` and `allocator_data` // that `Self::alloc_picture_data` set, which now get deallocated here. unsafe { (self.release_picture_callback)(&mut pic_c, self.cookie); } } }