• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::cell::RefCell;
6 use std::marker::PhantomData;
7 use std::rc::Rc;
8 use std::sync::Arc;
9 
10 use anyhow::anyhow;
11 use anyhow::Context as AnyhowContext;
12 use libva::{
13     Buffer, Context, Display, Picture, PictureEnd, PictureNew, PictureSync, Surface,
14     SurfaceMemoryDescriptor, VaError,
15 };
16 
17 use crate::decoder::stateless::StatelessBackendResult;
18 use crate::decoder::stateless::StatelessCodec;
19 use crate::decoder::stateless::StatelessDecoderBackend;
20 use crate::decoder::stateless::StatelessDecoderBackendPicture;
21 use crate::decoder::DecodedHandle as DecodedHandleTrait;
22 use crate::decoder::StreamInfo;
23 use crate::video_frame::VideoFrame;
24 use crate::DecodedFormat;
25 use crate::Rect;
26 use crate::Resolution;
27 
28 /// A decoded frame handle.
29 pub(crate) type DecodedHandle<V> = Rc<RefCell<VaapiDecodedHandle<V>>>;
30 
31 /// Gets the VASurfaceID for the given `picture`.
va_surface_id<V: VideoFrame>( handle: &Option<DecodedHandle<V>>, ) -> libva::VASurfaceID32 pub(crate) fn va_surface_id<V: VideoFrame>(
33     handle: &Option<DecodedHandle<V>>,
34 ) -> libva::VASurfaceID {
35     match handle {
36         None => libva::VA_INVALID_SURFACE,
37         Some(handle) => handle.borrow().surface().id(),
38     }
39 }
40 
41 impl<V: VideoFrame> DecodedHandleTrait for DecodedHandle<V> {
42     type Frame = V;
43 
video_frame(&self) -> Arc<Self::Frame>44     fn video_frame(&self) -> Arc<Self::Frame> {
45         self.borrow().backing_frame.clone()
46     }
47 
coded_resolution(&self) -> Resolution48     fn coded_resolution(&self) -> Resolution {
49         self.borrow().surface().size().into()
50     }
51 
display_resolution(&self) -> Resolution52     fn display_resolution(&self) -> Resolution {
53         self.borrow().display_resolution
54     }
55 
timestamp(&self) -> u6456     fn timestamp(&self) -> u64 {
57         self.borrow().timestamp()
58     }
59 
is_ready(&self) -> bool60     fn is_ready(&self) -> bool {
61         self.borrow().state.is_ready().unwrap_or(true)
62     }
63 
sync(&self) -> anyhow::Result<()>64     fn sync(&self) -> anyhow::Result<()> {
65         self.borrow_mut().sync().context("while syncing picture")?;
66 
67         Ok(())
68     }
69 }
70 
71 /// A trait for providing the basic information needed to setup libva for decoding.
72 pub(crate) trait VaStreamInfo {
73     /// Returns the VA profile of the stream.
va_profile(&self) -> anyhow::Result<i32>74     fn va_profile(&self) -> anyhow::Result<i32>;
75     /// Returns the RT format of the stream.
76     #[allow(dead_code)]
rt_format(&self) -> anyhow::Result<u32>77     fn rt_format(&self) -> anyhow::Result<u32>;
78     /// Returns the minimum number of surfaces required to decode the stream.
min_num_surfaces(&self) -> usize79     fn min_num_surfaces(&self) -> usize;
80     /// Returns the coded size of the surfaces required to decode the stream.
coded_size(&self) -> Resolution81     fn coded_size(&self) -> Resolution;
82     /// Returns the visible rectangle within the coded size for the stream.
visible_rect(&self) -> Rect83     fn visible_rect(&self) -> Rect;
84 }
85 
86 /// Rendering state of a VA picture.
87 enum PictureState<M: SurfaceMemoryDescriptor> {
88     Ready(Picture<PictureSync, Surface<M>>),
89     Pending(Picture<PictureEnd, Surface<M>>),
90     // Only set in sync when we take ownership of the VA picture.
91     Invalid,
92 }
93 
94 impl<M: SurfaceMemoryDescriptor> PictureState<M> {
95     /// Make sure that all pending operations on the picture have completed.
sync(&mut self) -> Result<(), VaError>96     fn sync(&mut self) -> Result<(), VaError> {
97         let res;
98 
99         (*self, res) = match std::mem::replace(self, PictureState::Invalid) {
100             state @ PictureState::Ready(_) => (state, Ok(())),
101             PictureState::Pending(picture) => match picture.sync() {
102                 Ok(picture) => (PictureState::Ready(picture), Ok(())),
103                 Err((e, picture)) => (PictureState::Pending(picture), Err(e)),
104             },
105             PictureState::Invalid => unreachable!(),
106         };
107 
108         res
109     }
110 
surface(&self) -> &Surface<M>111     fn surface(&self) -> &Surface<M> {
112         match self {
113             PictureState::Ready(picture) => picture.surface(),
114             PictureState::Pending(picture) => picture.surface(),
115             PictureState::Invalid => unreachable!(),
116         }
117     }
118 
timestamp(&self) -> u64119     fn timestamp(&self) -> u64 {
120         match self {
121             PictureState::Ready(picture) => picture.timestamp(),
122             PictureState::Pending(picture) => picture.timestamp(),
123             PictureState::Invalid => unreachable!(),
124         }
125     }
126 
is_ready(&self) -> Result<bool, VaError>127     fn is_ready(&self) -> Result<bool, VaError> {
128         match self {
129             PictureState::Ready(_) => Ok(true),
130             PictureState::Pending(picture) => picture
131                 .surface()
132                 .query_status()
133                 .map(|s| s == libva::VASurfaceStatus::VASurfaceReady),
134             PictureState::Invalid => unreachable!(),
135         }
136     }
137 
new_from_same_surface(&self, timestamp: u64) -> Picture<PictureNew, Surface<M>>138     fn new_from_same_surface(&self, timestamp: u64) -> Picture<PictureNew, Surface<M>> {
139         match &self {
140             PictureState::Ready(picture) => Picture::new_from_same_surface(timestamp, picture),
141             PictureState::Pending(picture) => Picture::new_from_same_surface(timestamp, picture),
142             PictureState::Invalid => unreachable!(),
143         }
144     }
145 }
146 
147 /// VA-API backend handle.
148 ///
149 /// This includes the VA picture which can be pending rendering or complete, as well as useful
150 /// meta-information.
151 pub struct VaapiDecodedHandle<V: VideoFrame> {
152     backing_frame: Arc<V>,
153     state: PictureState<<V as VideoFrame>::MemDescriptor>,
154     /// Actual resolution of the visible rectangle in the decoded buffer.
155     display_resolution: Resolution,
156 }
157 
158 impl<V: VideoFrame> VaapiDecodedHandle<V> {
159     /// Creates a new pending handle on `surface_id`.
160     fn new(picture: VaapiPicture<V>, display_resolution: Resolution) -> anyhow::Result<Self> {
161         let backing_frame = picture.backing_frame;
162         let picture = picture.picture.begin()?.render()?.end()?;
163         Ok(Self {
164             backing_frame: backing_frame,
165             state: PictureState::Pending(picture),
166             display_resolution: display_resolution,
167         })
168     }
169 
170     fn sync(&mut self) -> Result<(), VaError> {
171         self.state.sync()
172     }
173 
174     /// Creates a new picture from the surface backing the current one. Useful for interlaced
175     /// decoding. TODO: Do we need this for other purposes? We don't intend to support interlaced.
176     pub(crate) fn new_picture_from_same_surface(&self, timestamp: u64) -> VaapiPicture<V> {
177         VaapiPicture {
178             picture: self.state.new_from_same_surface(timestamp),
179             backing_frame: self.backing_frame.clone(),
180         }
181     }
182 
183     pub(crate) fn surface(&self) -> &Surface<<V as VideoFrame>::MemDescriptor> {
184         self.state.surface()
185     }
186 
187     /// Returns the timestamp of this handle.
188     fn timestamp(&self) -> u64 {
189         self.state.timestamp()
190     }
191 }
192 
193 pub struct VaapiBackend<V: VideoFrame> {
194     pub display: Rc<Display>,
195     pub context: Rc<Context>,
196     stream_info: StreamInfo,
197     // TODO: We should try to support context reuse
198     _supports_context_reuse: bool,
199     _phantom_data: PhantomData<V>,
200 }
201 
202 impl<V: VideoFrame> VaapiBackend<V> {
203     pub(crate) fn new(display: Rc<libva::Display>, supports_context_reuse: bool) -> Self {
204         let init_stream_info = StreamInfo {
205             format: DecodedFormat::NV12,
206             coded_resolution: Resolution::from((16, 16)),
207             display_resolution: Resolution::from((16, 16)),
208             min_num_frames: 1,
209         };
210         let config = display
211             .create_config(
212                 vec![libva::VAConfigAttrib {
213                     type_: libva::VAConfigAttribType::VAConfigAttribRTFormat,
214                     value: libva::VA_RT_FORMAT_YUV420,
215                 }],
216                 libva::VAProfile::VAProfileH264Main,
217                 libva::VAEntrypoint::VAEntrypointVLD,
218             )
219             .expect("Could not create initial VAConfig!");
220         let context = display
221             .create_context::<<V as VideoFrame>::MemDescriptor>(
222                 &config,
223                 init_stream_info.coded_resolution.width,
224                 init_stream_info.coded_resolution.height,
225                 None,
226                 true,
227             )
228             .expect("Could not create initial VAContext!");
229         Self {
230             display: display,
231             context: context,
232             _supports_context_reuse: supports_context_reuse,
233             stream_info: init_stream_info,
234             _phantom_data: Default::default(),
235         }
236     }
237 
238     pub(crate) fn new_sequence<StreamData>(
239         &mut self,
240         stream_params: &StreamData,
241     ) -> StatelessBackendResult<()>
242     where
243         for<'a> &'a StreamData: VaStreamInfo,
244     {
245         self.stream_info.display_resolution = Resolution::from(stream_params.visible_rect());
246         self.stream_info.coded_resolution = stream_params.coded_size().clone();
247         self.stream_info.min_num_frames = stream_params.min_num_surfaces();
248 
249         // TODO: Handle context re-use
250         // TODO: We should obtain RT_FORMAT from stream_info
251         let config = self
252             .display
253             .create_config(
254                 vec![libva::VAConfigAttrib {
255                     type_: libva::VAConfigAttribType::VAConfigAttribRTFormat,
256                     value: libva::VA_RT_FORMAT_YUV420,
257                 }],
258                 stream_params.va_profile().map_err(|_| anyhow!("Could not get VAProfile!"))?,
259                 libva::VAEntrypoint::VAEntrypointVLD,
260             )
261             .map_err(|_| anyhow!("Could not create VAConfig!"))?;
262         let context = self
263             .display
264             .create_context::<<V as VideoFrame>::MemDescriptor>(
265                 &config,
266                 self.stream_info.coded_resolution.width,
267                 self.stream_info.coded_resolution.height,
268                 None,
269                 true,
270             )
271             .map_err(|_| anyhow!("Could not create VAContext!"))?;
272         self.context = context;
273 
274         Ok(())
275     }
276 
277     pub(crate) fn process_picture<Codec: StatelessCodec>(
278         &mut self,
279         picture: VaapiPicture<V>,
280     ) -> StatelessBackendResult<<Self as StatelessDecoderBackend>::Handle>
281     where
282         Self: StatelessDecoderBackendPicture<Codec>,
283         for<'a> &'a Codec::FormatInfo: VaStreamInfo,
284     {
285         Ok(Rc::new(RefCell::new(VaapiDecodedHandle::new(
286             picture,
287             self.stream_info.display_resolution.clone(),
288         )?)))
289     }
290 }
291 
292 /// Shortcut for pictures used for the VAAPI backend.
293 pub struct VaapiPicture<V: VideoFrame> {
294     picture: Picture<PictureNew, Surface<V::MemDescriptor>>,
295     backing_frame: Arc<V>,
296 }
297 
298 impl<V: VideoFrame> VaapiPicture<V> {
299     pub fn new(timestamp: u64, context: Rc<Context>, backing_frame: V) -> Self {
300         let display = context.display();
301         let surface = backing_frame
302             .to_native_handle(display)
303             .expect("Failed to export video frame to vaapi picture!")
304             .into();
305         Self {
306             backing_frame: Arc::new(backing_frame),
307             picture: Picture::new(timestamp, context, surface),
308         }
309     }
310 
311     pub fn surface(&self) -> &Surface<V::MemDescriptor> {
312         self.picture.surface()
313     }
314 
315     pub fn add_buffer(&mut self, buffer: Buffer) {
316         self.picture.add_buffer(buffer)
317     }
318 }
319 
320 impl<V: VideoFrame> StatelessDecoderBackend for VaapiBackend<V> {
321     type Handle = DecodedHandle<V>;
322 
323     fn stream_info(&self) -> Option<&StreamInfo> {
324         Some(&self.stream_info)
325     }
326 
327     fn reset_backend(&mut self) -> anyhow::Result<()> {
328         //TODO(bchoobineh): Implement VAAPI DRC
329         Ok(())
330     }
331 }
332