// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::borrow::Borrow; use std::marker::PhantomData; use std::rc::Rc; use crate::bindings; use crate::buffer::Buffer; use crate::context::Context; use crate::surface::Surface; use crate::va_check; use crate::Image; use crate::SurfaceMemoryDescriptor; use crate::VaError; // Use the sealed trait pattern to make sure that new states are not created in caller code. More // information about the sealed trait pattern can be found at // mod private { pub trait Sealed {} } /// A `Picture` will only have valid YUV data after a sequence of operations are performed in a /// particular order. This order correspond to the following VA-API calls: `vaBeginPicture`, /// `vaRenderPicture`, `vaEndPicture` and `vaSyncSurface`. This trait enforces this ordering by /// implementing the Typestate pattern to constrain what operations are available in what particular /// states. /// /// The states for the state machine are: /// /// * PictureNew -> PictureBegin /// * PictureBegin -> PictureRender /// * PictureRender ->PictureEnd /// * PictureEnd -> PictureSync /// /// Where the surface can be reclaimed in both `PictureNew` and `PictureSync`, as either no /// operation took place (as in `PictureNew`), or it is guaranteed that the operation has already /// completed (as in `PictureSync`). /// /// More information about the Typestate pattern can be found at /// pub trait PictureState: private::Sealed {} /// Represents a `Picture` that has just been created. pub enum PictureNew {} impl PictureState for PictureNew {} impl private::Sealed for PictureNew {} /// Represents a `Picture` after `vaBeginPicture` has been called. pub enum PictureBegin {} impl PictureState for PictureBegin {} impl private::Sealed for PictureBegin {} /// Represents a `Picture` after `vaRenderPicture` has been called. pub enum PictureRender {} impl PictureState for PictureRender {} impl private::Sealed for PictureRender {} /// Represents a `Picture` after `vaEndPicture` has been called. pub enum PictureEnd {} impl PictureState for PictureEnd {} impl private::Sealed for PictureEnd {} /// Represents a `Picture` after `vaSyncSurface` has been called on the underlying surface. pub enum PictureSync {} impl PictureState for PictureSync {} impl private::Sealed for PictureSync {} /// Represents a state where one can reclaim the underlying `Surface` for this `Picture`. This is /// true when either no decoding has been initiated or, alternatively, when the decoding operation /// has completed for the underlying `vaSurface` pub trait PictureReclaimableSurface: PictureState + private::Sealed {} impl PictureReclaimableSurface for PictureNew {} impl PictureReclaimableSurface for PictureSync {} /// Inner type for [`Picture`], that is, the part that exists in all states. struct PictureInner { /// Timestamp of the picture. timestamp: u64, /// A context associated with this picture. context: Rc, /// Contains the buffers used to decode the data. buffers: Vec, /// Contains the actual decoded data. Note that the surface may be shared in /// interlaced decoding. surface: Rc, } /// A `Surface` that is being rendered into. /// /// This struct abstracts the decoding flow using `vaBeginPicture`, `vaRenderPicture`, /// `vaEndPicture` and `vaSyncSurface` in a type-safe way. /// /// The surface will have valid picture data after all the stages of decoding are called. /// /// The `T` generic parameter must be `Borrow>`, i.e. it can be [`Surface`] directly or /// some other type that contains one. /// /// No constraint on `T` is specified in this declaration because specifying it here would force us /// to add the generic argument of [`Surface`] to this type as well, turning it into a type with 3 /// generics, one of which is redundant. To avoid that we leave `T` unconstrained and instead /// constrain the methods that require to act on it as a [`Surface`]. pub struct Picture { inner: Box>, phantom: std::marker::PhantomData, } impl Picture { /// Creates a new Picture with a given `timestamp`. `surface` is the underlying surface that /// libva will render to. pub fn new(timestamp: u64, context: Rc, surface: T) -> Self where T: Borrow>, { Self { inner: Box::new(PictureInner { timestamp, context, buffers: Default::default(), surface: Rc::new(surface), }), phantom: PhantomData, } } /// Creates a new Picture with a given `timestamp` to identify it, /// reusing the Surface from `picture`. This is useful for interlaced /// decoding as one can render both fields to the same underlying surface. pub fn new_from_same_surface(timestamp: u64, picture: &Picture) -> Self { let context = Rc::clone(&picture.inner.context); Picture { inner: Box::new(PictureInner { timestamp, context, buffers: Default::default(), surface: Rc::clone(&picture.inner.surface), }), phantom: PhantomData, } } /// Add `buffer` to the picture. pub fn add_buffer(&mut self, buffer: Buffer) { self.inner.buffers.push(buffer); } /// Wrapper around `vaBeginPicture`. pub fn begin(self) -> Result, VaError> where T: Borrow>, { // Safe because `self.inner.context` represents a valid VAContext and // `self.inner.surface` represents a valid VASurface. let res = va_check(unsafe { bindings::vaBeginPicture( self.inner.context.display().handle(), self.inner.context.id(), self.surface().id(), ) }); res.map(|()| Picture { inner: self.inner, phantom: PhantomData, }) } } impl Picture { /// Wrapper around `vaRenderPicture`. pub fn render(self) -> Result, VaError> { // Safe because `self.inner.context` represents a valid `VAContext` and `self.inner.surface` // represents a valid `VASurface`. `buffers` point to a Rust struct and the vector length is // passed to the C function, so it is impossible to write past the end of the vector's // storage by mistake. va_check(unsafe { bindings::vaRenderPicture( self.inner.context.display().handle(), self.inner.context.id(), Buffer::as_id_vec(&self.inner.buffers).as_mut_ptr(), self.inner.buffers.len() as i32, ) }) .map(|()| Picture { inner: self.inner, phantom: PhantomData, }) } } impl Picture { /// Wrapper around `vaEndPicture`. pub fn end(self) -> Result, VaError> { // Safe because `self.inner.context` represents a valid `VAContext`. va_check(unsafe { bindings::vaEndPicture( self.inner.context.display().handle(), self.inner.context.id(), ) }) .map(|()| Picture { inner: self.inner, phantom: PhantomData, }) } } impl Picture { /// Syncs the picture, ensuring that all pending operations are complete when this call returns. pub fn sync( self, ) -> Result, (VaError, Self)> where T: Borrow>, { let res = self.surface().sync(); match res { Ok(()) => Ok(Picture { inner: self.inner, phantom: PhantomData, }), Err(e) => Err((e, self)), } } } impl Picture { /// Returns the timestamp of this picture. pub fn timestamp(&self) -> u64 { self.inner.timestamp } /// Returns a reference to the underlying `Surface`. /// /// If you are interested in obtaining the container of the `Surface`, use `as_ref()` instead. /// This is a convenience method to avoid having to call `borrow()` every time the surface is /// needed. pub fn surface(&self) -> &Surface where T: Borrow>, { self.as_ref().borrow() } } impl Picture { /// Reclaim ownership of the Surface this picture has been created from, consuming the picture /// in the process. Useful if the Surface is part of a pool. /// /// This will fail and return the passed object if there are more than one reference to the /// underlying surface. pub fn take_surface(self) -> Result { let inner = self.inner; match Rc::try_unwrap(inner.surface) { Ok(surface) => Ok(surface), Err(surface) => Err(Self { inner: Box::new(PictureInner { surface, context: inner.context, buffers: inner.buffers, timestamp: inner.timestamp, }), phantom: PhantomData, }), } } /// Create a new derived image from this `Picture` using `vaDeriveImage`. /// /// Derived images are a direct view (i.e. without any copy) on the buffer content of the /// `Picture`. On the other hand, not all `Pictures` can be derived. pub fn derive_image<'a, D: SurfaceMemoryDescriptor + 'a>( &'a self, visible_rect: (u32, u32), ) -> Result where T: Borrow>, { Image::derive_from(self.surface(), visible_rect) } /// Create new image from the `Picture` using `vaCreateImage` and `vaGetImage`. /// /// The image will contain a copy of the `Picture` in the desired `format` and `coded_resolution`. pub fn create_image<'a, D: SurfaceMemoryDescriptor + 'a>( &'a self, format: bindings::VAImageFormat, coded_resolution: (u32, u32), visible_rect: (u32, u32), ) -> Result where T: Borrow>, { Image::create_from(self.surface(), format, coded_resolution, visible_rect) } } impl AsRef for Picture { fn as_ref(&self) -> &T { (*self.inner.surface).borrow() } }