1use std::cell::RefCell;
6use std::collections::HashSet;
7use std::rc::Rc;
8
9use anyhow::anyhow;
10use anyhow::Context as AnyhowContext;
11use libva::Config;
12use libva::Context;
13use libva::Display;
14use libva::Image;
15use libva::Picture;
16use libva::PictureEnd;
17use libva::PictureNew;
18use libva::PictureSync;
19use libva::SurfaceMemoryDescriptor;
20use libva::VaError;
21
22use crate::backend::vaapi::p01x_to_i01x;
23use crate::backend::vaapi::surface_pool::PooledVaSurface;
24use crate::backend::vaapi::surface_pool::VaSurfacePool;
25use crate::backend::vaapi::va_rt_format_to_string;
26use crate::backend::vaapi::y21x_to_i21x;
27use crate::backend::vaapi::FormatMap;
28use crate::backend::vaapi::FORMAT_MAP;
29use crate::decoder::stateless::PoolLayer;
30use crate::decoder::stateless::StatelessBackendResult;
31use crate::decoder::stateless::StatelessCodec;
32use crate::decoder::stateless::StatelessDecoderBackend;
33use crate::decoder::stateless::StatelessDecoderBackendPicture;
34use crate::decoder::stateless::TryFormat;
35use crate::decoder::DecodedHandle as DecodedHandleTrait;
36use crate::decoder::DynHandle;
37use crate::decoder::FramePool;
38use crate::decoder::MappableHandle;
39use crate::decoder::StreamInfo;
40use crate::i4xx_copy;
41use crate::nv12_copy;
42use crate::y410_to_i410;
43use crate::DecodedFormat;
44use crate::Fourcc;
45use crate::Resolution;
46
47use super::supported_formats_for_rt_format;
48use super::y412_to_i412;
49
50pub(crate) type DecodedHandle<M> = Rc<RefCell<VaapiDecodedHandle<M>>>;
52
53pub(crate) fn va_surface_id<M: SurfaceMemoryDescriptor>(
55 handle: &Option<DecodedHandle<M>>,
56) -> libva::VASurfaceID {
57 match handle {
58 None => libva::constants::VA_INVALID_SURFACE,
59 Some(handle) => handle.borrow().surface().id(),
60 }
61}
62
63impl<M: SurfaceMemoryDescriptor> DecodedHandleTrait for DecodedHandle<M> {
64 type Descriptor = M;
65
66 fn coded_resolution(&self) -> Resolution {
67 self.borrow().surface().size().into()
68 }
69
70 fn display_resolution(&self) -> Resolution {
71 self.borrow().display_resolution
72 }
73
74 fn timestamp(&self) -> u64 {
75 self.borrow().timestamp()
76 }
77
78 fn dyn_picture<'a>(&'a self) -> Box<dyn DynHandle + 'a> {
79 Box::new(self.borrow())
80 }
81
82 fn is_ready(&self) -> bool {
83 self.borrow().state.is_ready().unwrap_or(true)
84 }
85
86 fn sync(&self) -> anyhow::Result<()> {
87 self.borrow_mut().sync().context("while syncing picture")?;
88
89 Ok(())
90 }
91
92 fn resource(&self) -> std::cell::Ref<M> {
93 std::cell::Ref::map(self.borrow(), |r| match &r.state {
94 PictureState::Ready(p) => p.surface().as_ref(),
95 PictureState::Pending(p) => p.surface().as_ref(),
96 PictureState::Invalid => unreachable!(),
97 })
98 }
99}
100
101pub(crate) trait VaStreamInfo {
103 fn va_profile(&self) -> anyhow::Result<i32>;
105 fn rt_format(&self) -> anyhow::Result<u32>;
107 fn min_num_surfaces(&self) -> usize;
109 fn coded_size(&self) -> (u32, u32);
111 fn visible_rect(&self) -> ((u32, u32), (u32, u32));
113}
114
115pub(crate) struct ParsedStreamMetadata {
116 pub(crate) context: Rc<Context>,
118 #[allow(dead_code)]
121 config: Config,
122 stream_info: StreamInfo,
124 map_format: Rc<libva::VAImageFormat>,
129 rt_format: u32,
131 profile: i32,
133}
134
135#[derive(Clone, Debug)]
137pub(crate) enum PoolCreationMode {
138 Highest,
141 Layers(Vec<Resolution>),
143}
144
145pub(crate) enum StreamMetadataState {
148 Unparsed,
150 Parsed(ParsedStreamMetadata),
153}
154
155type StreamStateWithPool<M> = (StreamMetadataState, Vec<VaSurfacePool<M>>);
156
157impl StreamMetadataState {
158 pub(crate) fn get_parsed(&self) -> anyhow::Result<&ParsedStreamMetadata> {
161 match self {
162 StreamMetadataState::Unparsed { .. } => Err(anyhow!("Stream metadata not parsed yet")),
163 StreamMetadataState::Parsed(parsed_metadata) => Ok(parsed_metadata),
164 }
165 }
166
167 fn open<S: VaStreamInfo, M: SurfaceMemoryDescriptor>(
169 display: &Rc<Display>,
170 hdr: S,
171 format_map: Option<&FormatMap>,
172 old_metadata_state: StreamMetadataState,
173 old_surface_pools: Vec<VaSurfacePool<M>>,
174 supports_context_reuse: bool,
175 pool_creation_mode: PoolCreationMode,
176 ) -> anyhow::Result<StreamStateWithPool<M>> {
177 let va_profile = hdr.va_profile()?;
178 let rt_format = hdr.rt_format()?;
179
180 let coded_resolution =
181 Resolution::from(hdr.coded_size()).round(crate::ResolutionRoundMode::Even);
182
183 let format_map = if let Some(format_map) = format_map {
184 format_map
185 } else {
186 FORMAT_MAP
188 .iter()
189 .find(|&map| map.rt_format == rt_format)
190 .ok_or(anyhow!(
191 "format {} is not supported by your hardware or by the implementation for the current codec",
192 va_rt_format_to_string(rt_format)
193 ))?
194 };
195
196 let map_format = display
197 .query_image_formats()?
198 .iter()
199 .find(|f| f.fourcc == format_map.va_fourcc)
200 .cloned()
201 .ok_or_else(|| {
202 anyhow!(
203 "fourcc {} is not supported by your hardware or by the implementation for the current codec",
204 Fourcc::from(format_map.va_fourcc)
205 )
206 })?;
207
208 let min_num_surfaces = hdr.min_num_surfaces();
209
210 let visible_rect = hdr.visible_rect();
211
212 let display_resolution = Resolution {
213 width: visible_rect.1 .0 - visible_rect.0 .0,
214 height: visible_rect.1 .1 - visible_rect.0 .1,
215 };
216
217 let layers = match pool_creation_mode {
218 PoolCreationMode::Highest => vec![coded_resolution],
219 PoolCreationMode::Layers(layers) => layers,
220 };
221
222 let (config, context, mut surface_pools) = match old_metadata_state {
223 StreamMetadataState::Parsed(old_state)
229 if old_state.stream_info.coded_resolution == coded_resolution
230 && old_state.rt_format == rt_format
231 && old_state.profile == va_profile =>
232 {
233 (old_state.config, old_state.context, old_surface_pools)
234 }
235 StreamMetadataState::Parsed(old_state)
238 if supports_context_reuse
239 && old_state.rt_format == rt_format
240 && old_state.profile == va_profile =>
241 {
242 (old_state.config, old_state.context, old_surface_pools)
243 }
244 _ => {
246 let config = display.create_config(
247 vec![libva::VAConfigAttrib {
248 type_: libva::VAConfigAttribType::VAConfigAttribRTFormat,
249 value: rt_format,
250 }],
251 va_profile,
252 libva::VAEntrypoint::VAEntrypointVLD,
253 )?;
254
255 let context = display.create_context::<M>(
256 &config,
257 coded_resolution.width,
258 coded_resolution.height,
259 None,
260 true,
261 )?;
262
263 let surface_pools = layers
264 .iter()
265 .map(|layer| {
266 VaSurfacePool::new(
267 Rc::clone(display),
268 rt_format,
269 Some(libva::UsageHint::USAGE_HINT_DECODER),
270 *layer,
271 )
272 })
273 .collect();
274
275 (config, context, surface_pools)
276 }
277 };
278
279 assert!(surface_pools.len() == layers.len());
281 for (pool, layer) in surface_pools.iter_mut().zip(layers.iter()) {
282 if !pool.coded_resolution().can_contain(*layer) {
283 pool.set_coded_resolution(*layer);
285 }
286 }
287
288 Ok((
289 StreamMetadataState::Parsed(ParsedStreamMetadata {
290 context,
291 config,
292 stream_info: StreamInfo {
293 format: match rt_format {
294 libva::constants::VA_RT_FORMAT_YUV420 => DecodedFormat::I420,
295 libva::constants::VA_RT_FORMAT_YUV422 => DecodedFormat::I422,
296 libva::constants::VA_RT_FORMAT_YUV444 => DecodedFormat::I444,
297 libva::constants::VA_RT_FORMAT_YUV420_10 => DecodedFormat::I010,
298 libva::constants::VA_RT_FORMAT_YUV420_12 => DecodedFormat::I012,
299 libva::constants::VA_RT_FORMAT_YUV422_10 => DecodedFormat::I210,
300 libva::constants::VA_RT_FORMAT_YUV422_12 => DecodedFormat::I212,
301 libva::constants::VA_RT_FORMAT_YUV444_10 => DecodedFormat::I410,
302 libva::constants::VA_RT_FORMAT_YUV444_12 => DecodedFormat::I412,
303 _ => panic!("unrecognized RT format {}", rt_format),
304 },
305 coded_resolution,
306 display_resolution,
307 min_num_frames: min_num_surfaces,
308 },
309 map_format: Rc::new(map_format),
310 rt_format,
311 profile: va_profile,
312 }),
313 surface_pools,
314 ))
315 }
316}
317
318enum PictureState<M: SurfaceMemoryDescriptor> {
320 Ready(Picture<PictureSync, PooledVaSurface<M>>),
321 Pending(Picture<PictureEnd, PooledVaSurface<M>>),
322 Invalid,
324}
325
326impl<M: SurfaceMemoryDescriptor> PictureState<M> {
327 fn sync(&mut self) -> Result<(), VaError> {
329 let res;
330
331 (*self, res) = match std::mem::replace(self, PictureState::Invalid) {
332 state @ PictureState::Ready(_) => (state, Ok(())),
333 PictureState::Pending(picture) => match picture.sync() {
334 Ok(picture) => (PictureState::Ready(picture), Ok(())),
335 Err((e, picture)) => (PictureState::Pending(picture), Err(e)),
336 },
337 PictureState::Invalid => unreachable!(),
338 };
339
340 res
341 }
342
343 fn surface(&self) -> &libva::Surface<M> {
344 match self {
345 PictureState::Ready(picture) => picture.surface(),
346 PictureState::Pending(picture) => picture.surface(),
347 PictureState::Invalid => unreachable!(),
348 }
349 }
350
351 fn timestamp(&self) -> u64 {
352 match self {
353 PictureState::Ready(picture) => picture.timestamp(),
354 PictureState::Pending(picture) => picture.timestamp(),
355 PictureState::Invalid => unreachable!(),
356 }
357 }
358
359 fn is_ready(&self) -> Result<bool, VaError> {
360 match self {
361 PictureState::Ready(_) => Ok(true),
362 PictureState::Pending(picture) => picture
363 .surface()
364 .query_status()
365 .map(|s| s == libva::VASurfaceStatus::VASurfaceReady),
366 PictureState::Invalid => unreachable!(),
367 }
368 }
369
370 fn new_from_same_surface(&self, timestamp: u64) -> Picture<PictureNew, PooledVaSurface<M>> {
371 match &self {
372 PictureState::Ready(picture) => Picture::new_from_same_surface(timestamp, picture),
373 PictureState::Pending(picture) => Picture::new_from_same_surface(timestamp, picture),
374 PictureState::Invalid => unreachable!(),
375 }
376 }
377}
378
379pub struct VaapiDecodedHandle<M: SurfaceMemoryDescriptor> {
384 state: PictureState<M>,
385 display_resolution: Resolution,
387 map_format: Rc<libva::VAImageFormat>,
389}
390
391impl<M: SurfaceMemoryDescriptor> VaapiDecodedHandle<M> {
392 fn new(
394 picture: Picture<PictureNew, PooledVaSurface<M>>,
395 metadata: &ParsedStreamMetadata,
396 ) -> anyhow::Result<Self> {
397 let picture = picture.begin()?.render()?.end()?;
398 Ok(Self {
399 state: PictureState::Pending(picture),
400 display_resolution: metadata.stream_info.display_resolution,
401 map_format: Rc::clone(&metadata.map_format),
402 })
403 }
404
405 fn sync(&mut self) -> Result<(), VaError> {
406 self.state.sync()
407 }
408
409 fn image(&self) -> anyhow::Result<Image> {
415 match &self.state {
416 PictureState::Ready(picture) => {
417 let image = picture.create_image(
419 *self.map_format,
420 self.surface().size(),
421 self.display_resolution.into(),
422 )?;
423
424 Ok(image)
425 }
426 PictureState::Pending(_) | PictureState::Invalid => {
428 Err(anyhow::anyhow!("picture is not in Ready state"))
429 }
430 }
431 }
432
433 pub(crate) fn new_picture_from_same_surface(
436 &self,
437 timestamp: u64,
438 ) -> Picture<PictureNew, PooledVaSurface<M>> {
439 self.state.new_from_same_surface(timestamp)
440 }
441
442 pub(crate) fn surface(&self) -> &libva::Surface<M> {
443 self.state.surface()
444 }
445
446 fn timestamp(&self) -> u64 {
448 self.state.timestamp()
449 }
450}
451
452impl<'a, M: SurfaceMemoryDescriptor> DynHandle for std::cell::Ref<'a, VaapiDecodedHandle<M>> {
453 fn dyn_mappable_handle<'b>(&'b self) -> anyhow::Result<Box<dyn MappableHandle + 'b>> {
454 self.image().map(|i| Box::new(i) as Box<dyn MappableHandle>)
455 }
456}
457
458impl<'a> MappableHandle for Image<'a> {
459 fn read(&mut self, buffer: &mut [u8]) -> anyhow::Result<()> {
460 let image_size = self.image_size();
461 let image_inner = self.image();
462
463 let display_resolution = self.display_resolution();
464 let width = display_resolution.0 as usize;
465 let height = display_resolution.1 as usize;
466
467 if buffer.len() != image_size {
468 return Err(anyhow!(
469 "buffer size is {} while image size is {}",
470 buffer.len(),
471 image_size
472 ));
473 }
474
475 let pitches = image_inner.pitches.map(|x| x as usize);
476 let offsets = image_inner.offsets.map(|x| x as usize);
477
478 match image_inner.format.fourcc {
479 libva::constants::VA_FOURCC_NV12 => {
480 nv12_copy(self.as_ref(), buffer, width, height, pitches, offsets);
481 }
482 libva::constants::VA_FOURCC_I420 => {
483 i4xx_copy(
484 self.as_ref(),
485 buffer,
486 width,
487 height,
488 pitches,
489 offsets,
490 (true, true),
491 );
492 }
493 libva::constants::VA_FOURCC_422H => {
494 i4xx_copy(
495 self.as_ref(),
496 buffer,
497 width,
498 height,
499 pitches,
500 offsets,
501 (true, false),
502 );
503 }
504 libva::constants::VA_FOURCC_444P => {
505 i4xx_copy(
506 self.as_ref(),
507 buffer,
508 width,
509 height,
510 pitches,
511 offsets,
512 (false, false),
513 );
514 }
515 libva::constants::VA_FOURCC_P010 => {
516 p01x_to_i01x(self.as_ref(), buffer, 10, width, height, pitches, offsets);
517 }
518 libva::constants::VA_FOURCC_P012 => {
519 p01x_to_i01x(self.as_ref(), buffer, 12, width, height, pitches, offsets);
520 }
521 libva::constants::VA_FOURCC_Y210 => {
522 y21x_to_i21x(self.as_ref(), buffer, 10, width, height, pitches, offsets);
523 }
524 libva::constants::VA_FOURCC_Y212 => {
525 y21x_to_i21x(self.as_ref(), buffer, 12, width, height, pitches, offsets);
526 }
527 libva::constants::VA_FOURCC_Y410 => {
528 y410_to_i410(self.as_ref(), buffer, width, height, pitches, offsets);
529 }
530 libva::constants::VA_FOURCC_Y412 => {
531 y412_to_i412(self.as_ref(), buffer, width, height, pitches, offsets);
532 }
533 _ => {
534 return Err(anyhow!(
535 "unsupported format 0x{:x}",
536 image_inner.format.fourcc
537 ))
538 }
539 }
540
541 Ok(())
542 }
543
544 fn image_size(&mut self) -> usize {
545 let image = self.image();
546 let display_resolution = self.display_resolution();
547 crate::decoded_frame_size(
548 (&image.format).try_into().unwrap(),
549 display_resolution.0 as usize,
550 display_resolution.1 as usize,
551 )
552 }
553}
554
555pub struct VaapiBackend<M>
556where
557 M: SurfaceMemoryDescriptor,
558{
559 display: Rc<Display>,
561 pub(crate) surface_pools: Vec<VaSurfacePool<M>>,
565 pub(crate) metadata_state: StreamMetadataState,
567 supports_context_reuse: bool,
570 pool_creation_mode: PoolCreationMode,
572}
573
574impl<M> VaapiBackend<M>
575where
576 M: SurfaceMemoryDescriptor + 'static,
577{
578 pub(crate) fn new(display: Rc<libva::Display>, supports_context_reuse: bool) -> Self {
579 let surface_pools = vec![VaSurfacePool::new(
581 Rc::clone(&display),
582 libva::constants::VA_RT_FORMAT_YUV420,
583 Some(libva::UsageHint::USAGE_HINT_DECODER),
584 Resolution::from((16, 16)),
585 )];
586
587 Self {
588 display,
589 surface_pools,
590 metadata_state: StreamMetadataState::Unparsed,
591 supports_context_reuse,
592 pool_creation_mode: PoolCreationMode::Highest,
593 }
594 }
595
596 pub(crate) fn new_sequence<StreamData>(
597 &mut self,
598 stream_params: &StreamData,
599 pool_creation_mode: PoolCreationMode,
600 ) -> StatelessBackendResult<()>
601 where
602 for<'a> &'a StreamData: VaStreamInfo,
603 {
604 let old_metadata_state =
605 std::mem::replace(&mut self.metadata_state, StreamMetadataState::Unparsed);
606
607 let old_surface_pools = self.surface_pools.drain(..).collect();
608 (self.metadata_state, self.surface_pools) = StreamMetadataState::open(
609 &self.display,
610 stream_params,
611 None,
612 old_metadata_state,
613 old_surface_pools,
614 self.supports_context_reuse,
615 pool_creation_mode.clone(),
616 )?;
617
618 self.pool_creation_mode = pool_creation_mode;
619
620 Ok(())
621 }
622
623 pub(crate) fn process_picture<Codec: StatelessCodec>(
624 &mut self,
625 picture: Picture<PictureNew, PooledVaSurface<M>>,
626 ) -> StatelessBackendResult<<Self as StatelessDecoderBackend>::Handle>
627 where
628 Self: StatelessDecoderBackendPicture<Codec>,
629 for<'a> &'a Codec::FormatInfo: VaStreamInfo,
630 {
631 let metadata = self.metadata_state.get_parsed()?;
632
633 Ok(Rc::new(RefCell::new(VaapiDecodedHandle::new(
634 picture, metadata,
635 )?)))
636 }
637
638 fn supported_formats_for_stream(&self) -> anyhow::Result<HashSet<DecodedFormat>> {
644 let metadata = self.metadata_state.get_parsed()?;
645 let image_formats = self.display.query_image_formats()?;
646
647 let formats = supported_formats_for_rt_format(
648 &self.display,
649 metadata.rt_format,
650 metadata.profile,
651 libva::VAEntrypoint::VAEntrypointVLD,
652 &image_formats,
653 )?;
654
655 Ok(formats.into_iter().map(|f| f.decoded_format).collect())
656 }
657
658 pub(crate) fn highest_pool(&mut self) -> &mut VaSurfacePool<M> {
659 self.surface_pools
661 .iter_mut()
662 .max_by_key(|p| p.coded_resolution().height)
663 .unwrap()
664 }
665
666 pub(crate) fn pool(&mut self, layer: Resolution) -> Option<&mut VaSurfacePool<M>> {
667 self.surface_pools
668 .iter_mut()
669 .find(|p| p.coded_resolution() == layer)
670 }
671}
672
673pub type VaapiPicture<M> = Picture<PictureNew, PooledVaSurface<M>>;
675
676impl<M> StatelessDecoderBackend for VaapiBackend<M>
677where
678 M: SurfaceMemoryDescriptor,
679{
680 type Handle = DecodedHandle<M>;
681
682 type FramePool = VaSurfacePool<M>;
683
684 fn frame_pool(&mut self, layer: PoolLayer) -> Vec<&mut Self::FramePool> {
685 if let PoolLayer::Highest = layer {
686 return vec![self
687 .surface_pools
688 .iter_mut()
689 .max_by_key(|other| other.coded_resolution().height)
690 .unwrap()];
691 }
692
693 self.surface_pools
694 .iter_mut()
695 .filter(|pool| {
696 match layer {
697 PoolLayer::Highest => unreachable!(),
698 PoolLayer::Layer(resolution) => pool.coded_resolution() == resolution,
699 PoolLayer::All => {
700 true
702 }
703 }
704 })
705 .collect()
706 }
707
708 fn stream_info(&self) -> Option<&StreamInfo> {
709 self.metadata_state
710 .get_parsed()
711 .ok()
712 .map(|m| &m.stream_info)
713 }
714}
715
716impl<Codec: StatelessCodec, M> TryFormat<Codec> for VaapiBackend<M>
717where
718 for<'a> &'a Codec::FormatInfo: VaStreamInfo,
720 M: SurfaceMemoryDescriptor + 'static,
721{
722 fn try_format(
723 &mut self,
724 format_info: &Codec::FormatInfo,
725 format: crate::DecodedFormat,
726 ) -> anyhow::Result<()> {
727 let supported_formats_for_stream = self.supported_formats_for_stream()?;
728
729 if supported_formats_for_stream.contains(&format) {
730 let map_format = FORMAT_MAP
731 .iter()
732 .find(|&map| map.decoded_format == format)
733 .ok_or_else(|| {
734 anyhow!(
735 "cannot find corresponding VA format for decoded format {:?}",
736 format
737 )
738 })?;
739
740 let old_metadata_state =
741 std::mem::replace(&mut self.metadata_state, StreamMetadataState::Unparsed);
742
743 let old_surface_pools = self.surface_pools.drain(..).collect();
754 (self.metadata_state, self.surface_pools) = StreamMetadataState::open(
755 &self.display,
756 format_info,
757 Some(map_format),
758 old_metadata_state,
759 old_surface_pools,
760 self.supports_context_reuse,
761 self.pool_creation_mode.clone(),
762 )?;
763
764 Ok(())
765 } else {
766 Err(anyhow!("Format {:?} is unsupported.", format))
767 }
768 }
769}