vapoursynth/
frame.rs

1//! VapourSynth frames.
2
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5use std::ptr::{self, NonNull};
6use std::slice;
7use vapoursynth_sys as ffi;
8
9use thiserror::Error;
10
11use crate::api::API;
12use crate::component::Component;
13use crate::core::CoreRef;
14use crate::format::Format;
15use crate::map::{MapRef, MapRefMut};
16use crate::video_info::Resolution;
17
18/// An error indicating that the frame data has non-zero padding.
19#[derive(Error, Debug, Clone, Copy, Eq, PartialEq)]
20#[error("Frame data has non-zero padding: {}", _0)]
21pub struct NonZeroPadding(usize);
22
23/// One frame of a clip.
24// This type is intended to be publicly used only in reference form.
25#[derive(Debug)]
26pub struct Frame<'core> {
27    // The actual mutability of this depends on whether it's accessed via `&Frame` or `&mut Frame`.
28    handle: NonNull<ffi::VSFrame>,
29    // The cached frame format for fast access.
30    format: Format<'core>,
31    _owner: PhantomData<&'core ()>,
32}
33
34/// A reference to a ref-counted frame.
35#[derive(Debug)]
36pub struct FrameRef<'core> {
37    // Only immutable references to this are allowed.
38    frame: Frame<'core>,
39}
40
41/// A reference to a mutable frame.
42#[derive(Debug)]
43pub struct FrameRefMut<'core> {
44    // Both mutable and immutable references to this are allowed.
45    frame: Frame<'core>,
46}
47
48unsafe impl<'core> Send for Frame<'core> {}
49unsafe impl<'core> Sync for Frame<'core> {}
50
51#[doc(hidden)]
52impl<'core> Deref for Frame<'core> {
53    type Target = ffi::VSFrame;
54
55    // Technically this should return `&'core`.
56    #[inline]
57    fn deref(&self) -> &Self::Target {
58        unsafe { self.handle.as_ref() }
59    }
60}
61
62#[doc(hidden)]
63impl<'core> DerefMut for Frame<'core> {
64    // Technically this should return `&'core`.
65    #[inline]
66    fn deref_mut(&mut self) -> &mut Self::Target {
67        unsafe { self.handle.as_mut() }
68    }
69}
70
71impl<'core> Drop for Frame<'core> {
72    #[inline]
73    fn drop(&mut self) {
74        unsafe {
75            API::get_cached().free_frame(self);
76        }
77    }
78}
79
80impl<'core> Clone for FrameRef<'core> {
81    #[inline]
82    fn clone(&self) -> Self {
83        unsafe {
84            let handle = API::get_cached().clone_frame(self);
85            Self {
86                frame: Frame::from_ptr(handle),
87            }
88        }
89    }
90}
91
92impl<'core> Deref for FrameRef<'core> {
93    type Target = Frame<'core>;
94
95    #[inline]
96    fn deref(&self) -> &Self::Target {
97        &self.frame
98    }
99}
100
101impl<'core> Deref for FrameRefMut<'core> {
102    type Target = Frame<'core>;
103
104    #[inline]
105    fn deref(&self) -> &Self::Target {
106        &self.frame
107    }
108}
109
110impl<'core> DerefMut for FrameRefMut<'core> {
111    #[inline]
112    fn deref_mut(&mut self) -> &mut Self::Target {
113        &mut self.frame
114    }
115}
116
117impl<'core> FrameRef<'core> {
118    /// Wraps `handle` in a `FrameRef`.
119    ///
120    /// # Safety
121    /// The caller must ensure `handle` and the lifetime is valid and API is cached.
122    #[inline]
123    pub(crate) unsafe fn from_ptr(handle: *const ffi::VSFrame) -> Self {
124        Self {
125            frame: Frame::from_ptr(handle),
126        }
127    }
128}
129
130impl<'core> FrameRefMut<'core> {
131    /// Wraps `handle` in a `FrameRefMut`.
132    ///
133    /// # Safety
134    /// The caller must ensure `handle` and the lifetime is valid and API is cached.
135    #[inline]
136    #[expect(dead_code)]
137    pub(crate) unsafe fn from_ptr(handle: *mut ffi::VSFrame) -> Self {
138        Self {
139            frame: Frame::from_ptr(handle),
140        }
141    }
142
143    /// Creates a copy of the given frame.
144    ///
145    /// The plane data is copy-on-write, so this isn't very expensive by itself.
146    ///
147    /// Judging by the underlying implementation, it seems that any valid `core` can be used.
148    #[inline]
149    pub fn copy_of(core: CoreRef, frame: &Frame<'core>) -> Self {
150        Self {
151            frame: unsafe { Frame::from_ptr(API::get_cached().copy_frame(frame, core.ptr())) },
152        }
153    }
154
155    /// Creates a new frame with uninitialized plane data.
156    ///
157    /// Optionally copies the frame properties from the provided `prop_src` frame.
158    ///
159    /// # Safety
160    /// The returned frame contains uninitialized plane data. This should be handled carefully. See
161    /// the docs for `std::mem::uninitialized()` for more information.
162    ///
163    /// # Panics
164    /// Panics if the given resolution has components that don't fit into an `i32`.
165    #[inline]
166    pub unsafe fn new_uninitialized(
167        core: CoreRef<'core>,
168        prop_src: Option<&Frame<'core>>,
169        format: Format<'core>,
170        resolution: Resolution,
171    ) -> Self {
172        assert!(resolution.width <= i32::MAX as usize);
173        assert!(resolution.height <= i32::MAX as usize);
174
175        Self {
176            frame: unsafe {
177                Frame::from_ptr(API::get_cached().new_video_frame(
178                    &format,
179                    resolution.width as i32,
180                    resolution.height as i32,
181                    prop_src.map(|f| f.deref() as _).unwrap_or(ptr::null()),
182                    core.ptr(),
183                ))
184            },
185        }
186    }
187}
188
189impl<'core> From<FrameRefMut<'core>> for FrameRef<'core> {
190    #[inline]
191    fn from(x: FrameRefMut<'core>) -> Self {
192        Self { frame: x.frame }
193    }
194}
195
196impl<'core> Frame<'core> {
197    /// Converts a pointer to a frame to a reference.
198    ///
199    /// # Safety
200    /// The caller needs to ensure the pointer and the lifetime is valid, and that the resulting
201    /// `Frame` gets put into `FrameRef` or `FrameRefMut` according to the input pointer
202    /// mutability.
203    #[inline]
204    pub(crate) unsafe fn from_ptr(handle: *const ffi::VSFrame) -> Self {
205        Self {
206            handle: NonNull::new_unchecked(handle as *mut ffi::VSFrame),
207            format: unsafe {
208                let ptr = API::get_cached().get_frame_format(&*handle);
209                Format::from_ptr(ptr)
210            },
211            _owner: PhantomData,
212        }
213    }
214
215    /// Returns the frame format.
216    #[inline]
217    pub fn format(&self) -> Format<'core> {
218        self.format
219    }
220
221    /// Returns the width of a plane, in pixels.
222    ///
223    /// The width depends on the plane number because of the possible chroma subsampling.
224    ///
225    /// # Panics
226    /// Panics if `plane >= format().plane_count()`.
227    #[inline]
228    pub fn width(&self, plane: usize) -> usize {
229        assert!(plane < self.format().plane_count());
230
231        unsafe { API::get_cached().get_frame_width(self, plane as i32) as usize }
232    }
233
234    /// Returns the height of a plane, in pixels.
235    ///
236    /// The height depends on the plane number because of the possible chroma subsampling.
237    ///
238    /// # Panics
239    /// Panics if `plane >= format().plane_count()`.
240    #[inline]
241    pub fn height(&self, plane: usize) -> usize {
242        assert!(plane < self.format().plane_count());
243
244        unsafe { API::get_cached().get_frame_height(self, plane as i32) as usize }
245    }
246
247    /// Returns the resolution of a plane.
248    ///
249    /// The resolution depends on the plane number because of the possible chroma subsampling.
250    ///
251    /// # Panics
252    /// Panics if `plane` is invalid for this frame.
253    #[inline]
254    pub fn resolution(&self, plane: usize) -> Resolution {
255        assert!(plane < self.format().plane_count());
256
257        Resolution {
258            width: self.width(plane),
259            height: self.height(plane),
260        }
261    }
262
263    /// Returns the distance in bytes between two consecutive lines of a plane.
264    ///
265    /// # Panics
266    /// Panics if `plane >= format().plane_count()`.
267    #[inline]
268    pub fn stride(&self, plane: usize) -> usize {
269        assert!(plane < self.format().plane_count());
270
271        unsafe { API::get_cached().get_frame_stride(self, plane as i32) as usize }
272    }
273
274    /// Returns a slice of a plane's pixel row.
275    ///
276    /// # Panics
277    /// Panics if the requested plane, row or component type is invalid.
278    #[inline]
279    pub fn plane_row<T: Component>(&self, plane: usize, row: usize) -> &[T] {
280        assert!(plane < self.format().plane_count());
281        assert!(row < self.height(plane));
282        assert!(T::is_valid(self.format()));
283
284        let stride = self.stride(plane);
285        let ptr = self.data_ptr(plane);
286
287        let offset = stride * row;
288        assert!(offset <= isize::MAX as usize);
289        let offset = offset as isize;
290
291        let row_ptr = unsafe { ptr.offset(offset) };
292        let width = self.width(plane);
293
294        unsafe { slice::from_raw_parts(row_ptr as *const T, width) }
295    }
296
297    /// Returns a mutable slice of a plane's pixel row.
298    ///
299    /// # Panics
300    /// Panics if the requested plane, row or component type is invalid.
301    #[inline]
302    pub fn plane_row_mut<T: Component>(&mut self, plane: usize, row: usize) -> &mut [T] {
303        assert!(plane < self.format().plane_count());
304        assert!(row < self.height(plane));
305        assert!(T::is_valid(self.format()));
306
307        let stride = self.stride(plane);
308        let ptr = self.data_ptr_mut(plane);
309
310        let offset = stride * row;
311        assert!(offset <= isize::MAX as usize);
312        let offset = offset as isize;
313
314        let row_ptr = unsafe { ptr.offset(offset) };
315        let width = self.width(plane);
316
317        unsafe { slice::from_raw_parts_mut(row_ptr as *mut T, width) }
318    }
319
320    /// Returns a slice of the plane's pixels.
321    ///
322    /// The length of the returned slice is `height() * width()`. If the pixel data has non-zero
323    /// padding (that is, `stride()` is larger than `width()`), an error is returned, since
324    /// returning the data slice would open access to uninitialized bytes.
325    ///
326    /// # Panics
327    /// Panics if the requested plane or component type is invalid.
328    pub fn plane<T: Component>(&self, plane: usize) -> Result<&[T], NonZeroPadding> {
329        assert!(plane < self.format().plane_count());
330        assert!(T::is_valid(self.format()));
331
332        let stride = self.stride(plane);
333        let width_in_bytes = self.width(plane) * usize::from(self.format().bytes_per_sample());
334        if stride != width_in_bytes {
335            return Err(NonZeroPadding(stride - width_in_bytes));
336        }
337
338        let height = self.height(plane);
339        let length = height * self.width(plane);
340        let ptr = self.data_ptr(plane);
341
342        Ok(unsafe { slice::from_raw_parts(ptr as *const T, length) })
343    }
344
345    /// Returns a mutable slice of the plane's pixels.
346    ///
347    /// The length of the returned slice is `height() * width()`. If the pixel data has non-zero
348    /// padding (that is, `stride()` is larger than `width()`), an error is returned, since
349    /// returning the data slice would open access to uninitialized bytes.
350    ///
351    /// # Panics
352    /// Panics if the requested plane or component type is invalid.
353    pub fn plane_mut<T: Component>(&mut self, plane: usize) -> Result<&mut [T], NonZeroPadding> {
354        assert!(plane < self.format().plane_count());
355        assert!(T::is_valid(self.format()));
356
357        let stride = self.stride(plane);
358        let width_in_bytes = self.width(plane) * usize::from(self.format().bytes_per_sample());
359        if stride != width_in_bytes {
360            return Err(NonZeroPadding(stride - width_in_bytes));
361        }
362
363        let height = self.height(plane);
364        let length = height * self.width(plane);
365        let ptr = self.data_ptr_mut(plane);
366
367        Ok(unsafe { slice::from_raw_parts_mut(ptr as *mut T, length) })
368    }
369
370    /// Returns a pointer to the plane's pixels.
371    ///
372    /// The pointer points to an array with a length of `height() * stride()` and is valid for as
373    /// long as the frame is alive.
374    ///
375    /// # Panics
376    /// Panics if `plane >= format().plane_count()`.
377    #[inline]
378    pub fn data_ptr(&self, plane: usize) -> *const u8 {
379        assert!(plane < self.format().plane_count());
380
381        unsafe { API::get_cached().get_frame_read_ptr(self, plane as i32) }
382    }
383
384    /// Returns a mutable pointer to the plane's pixels.
385    ///
386    /// The pointer points to an array with a length of `height() * stride()` and is valid for as
387    /// long as the frame is alive.
388    ///
389    /// # Panics
390    /// Panics if `plane >= format().plane_count()`.
391    #[inline]
392    pub fn data_ptr_mut(&mut self, plane: usize) -> *mut u8 {
393        assert!(plane < self.format().plane_count());
394
395        unsafe { API::get_cached().get_frame_write_ptr(self, plane as i32) }
396    }
397
398    /// Returns a slice of a plane's pixel row.
399    ///
400    /// The length of the returned slice is equal to `width() * format().bytes_per_sample()`.
401    ///
402    /// # Panics
403    /// Panics if `plane >= format().plane_count()` or if `row >= height()`.
404    pub fn data_row(&self, plane: usize, row: usize) -> &[u8] {
405        assert!(plane < self.format().plane_count());
406        assert!(row < self.height(plane));
407
408        let stride = self.stride(plane);
409        let ptr = self.data_ptr(plane);
410
411        let offset = stride * row;
412        assert!(offset <= isize::MAX as usize);
413        let offset = offset as isize;
414
415        let row_ptr = unsafe { ptr.offset(offset) };
416        let width = self.width(plane) * usize::from(self.format().bytes_per_sample());
417
418        unsafe { slice::from_raw_parts(row_ptr, width) }
419    }
420
421    /// Returns a mutable slice of a plane's pixel row.
422    ///
423    /// The length of the returned slice is equal to `width() * format().bytes_per_sample()`.
424    ///
425    /// # Panics
426    /// Panics if `plane >= format().plane_count()` or if `row >= height()`.
427    pub fn data_row_mut(&mut self, plane: usize, row: usize) -> &mut [u8] {
428        assert!(plane < self.format().plane_count());
429        assert!(row < self.height(plane));
430
431        let stride = self.stride(plane);
432        let ptr = self.data_ptr_mut(plane);
433
434        let offset = stride * row;
435        assert!(offset <= isize::MAX as usize);
436        let offset = offset as isize;
437
438        let row_ptr = unsafe { ptr.offset(offset) };
439        let width = self.width(plane) * usize::from(self.format().bytes_per_sample());
440
441        unsafe { slice::from_raw_parts_mut(row_ptr, width) }
442    }
443
444    /// Returns a slice of the plane's pixels.
445    ///
446    /// The length of the returned slice is `height() * width() * format().bytes_per_sample()`. If
447    /// the pixel data has non-zero padding (that is, `stride()` is larger than `width()`), an
448    /// error is returned, since returning the data slice would open access to uninitialized bytes.
449    ///
450    /// # Panics
451    /// Panics if `plane >= format().plane_count()` or if `row >= height()`.
452    pub fn data(&self, plane: usize) -> Result<&[u8], NonZeroPadding> {
453        assert!(plane < self.format().plane_count());
454
455        let stride = self.stride(plane);
456        let width = self.width(plane) * usize::from(self.format().bytes_per_sample());
457        if stride != width {
458            return Err(NonZeroPadding(stride - width));
459        }
460
461        let height = self.height(plane);
462        let length = height * stride;
463        let ptr = self.data_ptr(plane);
464
465        Ok(unsafe { slice::from_raw_parts(ptr, length) })
466    }
467
468    /// Returns a mutable slice of the plane's pixels.
469    ///
470    /// The length of the returned slice is `height() * width() * format().bytes_per_sample()`. If
471    /// the pixel data has non-zero padding (that is, `stride()` is larger than `width()`), an
472    /// error is returned, since returning the data slice would open access to uninitialized bytes.
473    ///
474    /// # Panics
475    /// Panics if `plane >= format().plane_count()` or if `row >= height()`.
476    pub fn data_mut(&mut self, plane: usize) -> Result<&mut [u8], NonZeroPadding> {
477        assert!(plane < self.format().plane_count());
478
479        let stride = self.stride(plane);
480        let width = self.width(plane) * usize::from(self.format().bytes_per_sample());
481        if stride != width {
482            return Err(NonZeroPadding(stride - width));
483        }
484
485        let height = self.height(plane);
486        let length = height * stride;
487        let ptr = self.data_ptr_mut(plane);
488
489        Ok(unsafe { slice::from_raw_parts_mut(ptr, length) })
490    }
491
492    /// Returns a map of frame's properties.
493    #[inline]
494    pub fn props(&self) -> MapRef<'_, '_> {
495        unsafe { MapRef::from_ptr(API::get_cached().get_frame_props_ro(self)) }
496    }
497
498    /// Returns a mutable map of frame's properties.
499    #[inline]
500    pub fn props_mut(&mut self) -> MapRefMut<'_, '_> {
501        unsafe { MapRefMut::from_ptr(API::get_cached().get_frame_props_rw(self)) }
502    }
503}