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