Path: blob/main/devices/src/virtio/video/decoder/backend/vaapi.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34#![deny(missing_docs)]56use std::collections::btree_map::Entry;7use std::collections::BTreeMap;8use std::collections::VecDeque;9use std::os::fd::FromRawFd;10use std::os::fd::OwnedFd;11use std::rc::Rc;1213use anyhow::anyhow;14use anyhow::Result;15use base::IntoRawDescriptor;16use base::MappedRegion;17use base::MemoryMappingArena;18use cros_codecs::decoder::stateless::h264::H264;19use cros_codecs::decoder::stateless::h265::H265;20use cros_codecs::decoder::stateless::vp8::Vp8;21use cros_codecs::decoder::stateless::vp9::Vp9;22use cros_codecs::decoder::stateless::DecodeError;23use cros_codecs::decoder::stateless::StatelessVideoDecoder;24use cros_codecs::decoder::DecodedHandle;25use cros_codecs::libva;26use cros_codecs::libva::Display;27use cros_codecs::multiple_desc_type;28use cros_codecs::utils::DmabufFrame;29use cros_codecs::DecodedFormat;30use cros_codecs::FrameLayout;31use cros_codecs::PlaneLayout;3233use crate::virtio::video::decoder::Capability;34use crate::virtio::video::decoder::DecoderBackend;35use crate::virtio::video::decoder::DecoderEvent;36use crate::virtio::video::decoder::DecoderSession;37use crate::virtio::video::error::VideoError;38use crate::virtio::video::error::VideoResult;39use crate::virtio::video::format::Format;40use crate::virtio::video::format::FormatDesc;41use crate::virtio::video::format::FormatRange;42use crate::virtio::video::format::FrameFormat;43use crate::virtio::video::format::Level;44use crate::virtio::video::format::Profile;45use crate::virtio::video::format::Rect;46use crate::virtio::video::resource::BufferHandle;47use crate::virtio::video::resource::GuestMemHandle;48use crate::virtio::video::resource::GuestResource;49use crate::virtio::video::resource::GuestResourceHandle;50use crate::virtio::video::utils::EventQueue;5152/// A guest memory descriptor that uses a managed buffer as a shadow that will be copied into the53/// guest memory once decoding is over.54struct GuestMemDescriptor(GuestMemHandle);5556impl libva::SurfaceMemoryDescriptor for GuestMemDescriptor {57fn add_attrs(58&mut self,59attrs: &mut Vec<libva::VASurfaceAttrib>,60) -> Option<Box<dyn std::any::Any>> {61// Decode into a managed buffer.62().add_attrs(attrs)63}64}6566multiple_desc_type! {67enum BufferDescriptor {68GuestMem(GuestMemDescriptor),69Dmabuf(DmabufFrame),70}71}7273struct BufferDescWithPicId {74desc: BufferDescriptor,75picture_buffer_id: i32,76}7778impl libva::SurfaceMemoryDescriptor for BufferDescWithPicId {79fn add_attrs(80&mut self,81attrs: &mut Vec<libva::VASurfaceAttrib>,82) -> Option<Box<dyn std::any::Any>> {83self.desc.add_attrs(attrs)84}85}8687/// Represents a buffer we have not yet sent to the accelerator.88struct PendingJob {89resource_id: u32,90timestamp: u64,91resource: GuestResourceHandle,92offset: usize,93bytes_used: usize,94remaining: usize,95}9697impl TryFrom<DecodedFormat> for Format {98type Error = anyhow::Error;99100fn try_from(value: DecodedFormat) -> Result<Self, Self::Error> {101match value {102DecodedFormat::NV12 => Ok(Format::NV12),103_ => Err(anyhow!("Unsupported format")),104}105}106}107108impl TryFrom<Format> for DecodedFormat {109type Error = anyhow::Error;110111fn try_from(value: Format) -> Result<Self, Self::Error> {112match value {113Format::NV12 => Ok(DecodedFormat::NV12),114_ => Err(anyhow!("Unsupported format")),115}116}117}118119impl TryFrom<libva::VAProfile::Type> for Profile {120type Error = anyhow::Error;121122fn try_from(value: libva::VAProfile::Type) -> Result<Self, Self::Error> {123match value {124libva::VAProfile::VAProfileH264Baseline => Ok(Self::H264Baseline),125libva::VAProfile::VAProfileH264Main => Ok(Self::H264Main),126libva::VAProfile::VAProfileH264High => Ok(Self::H264High),127libva::VAProfile::VAProfileH264StereoHigh => Ok(Self::H264StereoHigh),128libva::VAProfile::VAProfileH264MultiviewHigh => Ok(Self::H264MultiviewHigh),129libva::VAProfile::VAProfileHEVCMain => Ok(Self::HevcMain),130libva::VAProfile::VAProfileHEVCMain10 => Ok(Self::HevcMain10),131libva::VAProfile::VAProfileVP8Version0_3 => Ok(Self::VP8Profile0),132libva::VAProfile::VAProfileVP9Profile0 => Ok(Self::VP9Profile0),133libva::VAProfile::VAProfileVP9Profile1 => Ok(Self::VP9Profile1),134libva::VAProfile::VAProfileVP9Profile2 => Ok(Self::VP9Profile2),135libva::VAProfile::VAProfileVP9Profile3 => Ok(Self::VP9Profile3),136_ => Err(anyhow!(137"Conversion failed for unexpected VAProfile: {}",138value139)),140}141}142}143144/// The state for the output queue containing the buffers that will receive the145/// decoded data.146enum OutputQueueState {147/// Waiting for the client to call `set_output_buffer_count`.148AwaitingBufferCount,149/// Codec is capable of decoding frames.150Decoding,151/// Dynamic Resolution Change - we can still accept buffers in the old152/// format, but are waiting for new parameters before doing any decoding.153Drc,154}155156///A safe decoder abstraction over libva for a single vaContext157pub struct VaapiDecoder {158/// The capabilities for the decoder159caps: Capability,160}161162// The VA capabilities for the coded side163struct CodedCap {164profile: libva::VAProfile::Type,165max_width: u32,166max_height: u32,167}168169// The VA capabilities for the raw side170struct RawCap {171fourcc: u32,172min_width: u32,173min_height: u32,174max_width: u32,175max_height: u32,176}177178impl VaapiDecoder {179// Query the capabilities for the coded format180fn get_coded_cap(181display: &libva::Display,182profile: libva::VAProfile::Type,183) -> Result<CodedCap> {184let mut attrs = vec![185libva::VAConfigAttrib {186type_: libva::VAConfigAttribType::VAConfigAttribMaxPictureWidth,187value: 0,188},189libva::VAConfigAttrib {190type_: libva::VAConfigAttribType::VAConfigAttribMaxPictureHeight,191value: 0,192},193];194195display.get_config_attributes(profile, libva::VAEntrypoint::VAEntrypointVLD, &mut attrs)?;196197let mut max_width = 1u32;198let mut max_height = 1u32;199200for attr in &attrs {201if attr.value == libva::constants::VA_ATTRIB_NOT_SUPPORTED {202continue;203}204205match attr.type_ {206libva::VAConfigAttribType::VAConfigAttribMaxPictureWidth => max_width = attr.value,207libva::VAConfigAttribType::VAConfigAttribMaxPictureHeight => {208max_height = attr.value209}210211_ => panic!("Unexpected VAConfigAttribType {}", attr.type_),212}213}214215Ok(CodedCap {216profile,217max_width,218max_height,219})220}221222// Query the capabilities for the raw format223fn get_raw_caps(display: Rc<libva::Display>, coded_cap: &CodedCap) -> Result<Vec<RawCap>> {224let mut raw_caps = Vec::new();225226let mut config = display.create_config(227vec![],228coded_cap.profile,229libva::VAEntrypoint::VAEntrypointVLD,230)?;231232let fourccs = config.query_surface_attributes_by_type(233libva::VASurfaceAttribType::VASurfaceAttribPixelFormat,234)?;235236for fourcc in fourccs {237let fourcc = match fourcc {238libva::GenericValue::Integer(i) => i as u32,239other => panic!("Unexpected VAGenericValue {other:?}"),240};241242let min_width = config.query_surface_attributes_by_type(243libva::VASurfaceAttribType::VASurfaceAttribMinWidth,244)?;245246let min_width = match min_width.first() {247Some(libva::GenericValue::Integer(i)) => *i as u32,248Some(other) => panic!("Unexpected VAGenericValue {other:?}"),249None => 1,250};251252let min_height = config.query_surface_attributes_by_type(253libva::VASurfaceAttribType::VASurfaceAttribMinHeight,254)?;255let min_height = match min_height.first() {256Some(libva::GenericValue::Integer(i)) => *i as u32,257Some(other) => panic!("Unexpected VAGenericValue {other:?}"),258None => 1,259};260261let max_width = config.query_surface_attributes_by_type(262libva::VASurfaceAttribType::VASurfaceAttribMaxWidth,263)?;264let max_width = match max_width.first() {265Some(libva::GenericValue::Integer(i)) => *i as u32,266Some(other) => panic!("Unexpected VAGenericValue {other:?}"),267None => coded_cap.max_width,268};269270let max_height = config.query_surface_attributes_by_type(271libva::VASurfaceAttribType::VASurfaceAttribMaxHeight,272)?;273let max_height = match max_height.first() {274Some(libva::GenericValue::Integer(i)) => *i as u32,275Some(other) => panic!("Unexpected VAGenericValue {other:?}"),276None => coded_cap.max_height,277};278279raw_caps.push(RawCap {280fourcc,281min_width,282min_height,283max_width,284max_height,285});286}287288Ok(raw_caps)289}290291/// Creates a new instance of the Vaapi decoder.292pub fn new() -> Result<Self> {293let display = libva::Display::open().ok_or_else(|| anyhow!("failed to open VA display"))?;294295let va_profiles = display.query_config_profiles()?;296297let mut in_fmts = Vec::new();298let mut out_fmts = Vec::new();299let mut profiles_map: BTreeMap<Format, Vec<Profile>> = Default::default();300301// VA has no API for querying the levels supported by the driver.302// vaQueryProcessingRate is close, but not quite a solution here303// for all codecs.304let levels: BTreeMap<Format, Vec<Level>> = Default::default();305306for va_profile in va_profiles {307let mut profiles = Vec::new();308309let entrypoints = display.query_config_entrypoints(va_profile)?;310if !entrypoints.contains(&libva::VAEntrypoint::VAEntrypointVLD) {311// All formats we are aiming to support require312// VAEntrypointVLD.313continue;314}315316let profile = match Profile::try_from(va_profile) {317Ok(p) => p,318// Skip if we cannot convert to a valid virtio format319Err(_) => continue,320};321322// Manually push all VP8 profiles, since VA exposes only a single323// VP8 profile for all of these324if va_profile == libva::VAProfile::VAProfileVP8Version0_3 {325profiles.push(Profile::VP8Profile0);326profiles.push(Profile::VP8Profile1);327profiles.push(Profile::VP8Profile2);328profiles.push(Profile::VP8Profile3);329} else {330profiles.push(profile);331}332333let coded_cap = VaapiDecoder::get_coded_cap(display.as_ref(), va_profile)?;334let raw_caps = VaapiDecoder::get_raw_caps(Rc::clone(&display), &coded_cap)?;335336let coded_frame_fmt = FrameFormat {337width: FormatRange {338min: 1,339max: coded_cap.max_width,340step: 1,341},342343height: FormatRange {344min: 1,345max: coded_cap.max_height,346step: 1,347},348349bitrates: Default::default(),350};351352let coded_format = profile.to_format();353match profiles_map.entry(coded_format) {354Entry::Vacant(e) => {355e.insert(profiles);356}357Entry::Occupied(mut ps) => {358ps.get_mut().push(profile);359}360}361362let mut n_out = 0;363for raw_cap in raw_caps {364if raw_cap.fourcc != libva::constants::VA_FOURCC_NV12 {365// Apparently only NV12 is currently supported by virtio video366continue;367}368369let raw_frame_fmt = FrameFormat {370width: FormatRange {371min: raw_cap.min_width,372max: raw_cap.max_width,373step: 1,374},375376height: FormatRange {377min: raw_cap.min_height,378max: raw_cap.max_height,379step: 1,380},381382bitrates: Default::default(),383};384385out_fmts.push(FormatDesc {386mask: 0,387format: Format::NV12,388frame_formats: vec![raw_frame_fmt],389plane_align: 1,390});391392n_out += 1;393}394395let mask = !(u64::MAX << n_out) << (out_fmts.len() - n_out);396397if mask != 0 {398in_fmts.push(FormatDesc {399mask,400format: coded_format,401frame_formats: vec![coded_frame_fmt],402plane_align: 1,403});404}405}406407Ok(Self {408caps: Capability::new(in_fmts, out_fmts, profiles_map, levels),409})410}411}412413#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]414pub struct Resolution {415width: u32,416height: u32,417}418419trait AsBufferHandle {420type BufferHandle: BufferHandle;421fn as_buffer_handle(&self) -> &Self::BufferHandle;422}423424impl AsBufferHandle for GuestResource {425type BufferHandle = GuestResourceHandle;426427fn as_buffer_handle(&self) -> &Self::BufferHandle {428&self.handle429}430}431432impl AsBufferHandle for GuestMemHandle {433type BufferHandle = Self;434435fn as_buffer_handle(&self) -> &Self::BufferHandle {436self437}438}439440impl AsBufferHandle for GuestResourceHandle {441type BufferHandle = Self;442443fn as_buffer_handle(&self) -> &Self::BufferHandle {444self445}446}447448/// A convenience type implementing persistent slice access for BufferHandles.449struct BufferMapping<'a, T: AsBufferHandle> {450#[allow(dead_code)]451/// The underlying resource. Must be kept so as not to drop the BufferHandle452resource: &'a T,453/// The mapping that backs the underlying slices returned by AsRef and AsMut454mapping: MemoryMappingArena,455}456457impl<'a, T: AsBufferHandle> BufferMapping<'a, T> {458/// Creates a new BufferMap459pub fn new(resource: &'a T, offset: usize, size: usize) -> Result<Self> {460let mapping = resource.as_buffer_handle().get_mapping(offset, size)?;461462Ok(Self { resource, mapping })463}464}465466impl<T: AsBufferHandle> AsRef<[u8]> for BufferMapping<'_, T> {467fn as_ref(&self) -> &[u8] {468let mapping = &self.mapping;469// SAFETY:470// Safe because the mapping is linear and we own it, so it will not be unmapped during471// the lifetime of this slice.472unsafe { std::slice::from_raw_parts(mapping.as_ptr(), mapping.size()) }473}474}475476impl<T: AsBufferHandle> AsMut<[u8]> for BufferMapping<'_, T> {477fn as_mut(&mut self) -> &mut [u8] {478let mapping = &self.mapping;479// SAFETY:480// Safe because the mapping is linear and we own it, so it will not be unmapped during481// the lifetime of this slice.482unsafe { std::slice::from_raw_parts_mut(mapping.as_ptr(), mapping.size()) }483}484}485486/// A frame that is currently not available for being decoded into, either because it has been487/// decoded and is waiting for us to release it (`Decoded`), or because we temporarily removed it488/// from the decoder pool after a reset and are waiting for the client to tell us we can use it489/// (`Held`).490#[allow(dead_code)] // TODO: b/344974550491enum BorrowedFrame {492Decoded(Box<dyn DecodedHandle<Descriptor = BufferDescWithPicId>>),493Held(Box<dyn AsRef<BufferDescWithPicId>>),494}495496/// A decoder session for the libva backend497pub struct VaapiDecoderSession {498/// The implementation for the codec specific logic.499codec: Box<dyn StatelessVideoDecoder<BufferDescWithPicId>>,500/// The state for the output queue. Updated when `set_output_buffer_count`501/// is called or when we detect a dynamic resolution change.502output_queue_state: OutputQueueState,503/// Frames currently held by us, indexed by `picture_buffer_id`.504held_frames: BTreeMap<i32, BorrowedFrame>,505/// Queue containing the buffers we have not yet submitted to the codec.506submit_queue: VecDeque<PendingJob>,507/// The event queue we can use to signal new events.508event_queue: EventQueue<DecoderEvent>,509/// Whether the decoder is currently flushing.510flushing: bool,511}512513impl VaapiDecoderSession {514/// Copy raw decoded data from `image` into the output buffer515fn output_picture(516decoded_frame: &dyn DecodedHandle<Descriptor = BufferDescWithPicId>,517event_queue: &mut EventQueue<DecoderEvent>,518) -> Result<()> {519let timestamp = decoded_frame.timestamp();520521let buffer_desc = decoded_frame.resource();522let picture_buffer_id = buffer_desc.picture_buffer_id;523524// Sync the frame if it is in guest memory, as we are going to map and read it.525// This statement is in its own block so we can drop the `buffer_desc` reference526// before calling `sync`, which does a mutable borrow.527if let BufferDescriptor::GuestMem(_) = &buffer_desc.desc {528drop(buffer_desc);529decoded_frame.sync()?;530}531532// Copy guest memory buffers into their destination.533if let BufferDescriptor::GuestMem(handle) = &decoded_frame.resource().desc {534let picture = decoded_frame.dyn_picture();535let mut backend_handle = picture.dyn_mappable_handle()?;536let buffer_size = backend_handle.image_size();537538// Get a mapping from the start of the buffer to the size of the539// underlying decoded data in the Image.540let mut output_map = BufferMapping::new(&handle.0, 0, buffer_size)?;541let output_bytes = output_map.as_mut();542543backend_handle.read(output_bytes)?;544}545546// Say that we are done decoding this picture.547event_queue548.queue_event(DecoderEvent::PictureReady {549picture_buffer_id,550timestamp,551})552.map_err(|e| {553VideoError::BackendFailure(anyhow!("Can't queue the PictureReady event {}", e))554})?;555556Ok(())557}558559fn try_emit_flush_completed(&mut self) -> Result<()> {560if self.submit_queue.is_empty() {561self.flushing = false;562563let event_queue = &mut self.event_queue;564565event_queue566.queue_event(DecoderEvent::FlushCompleted(Ok(())))567.map_err(|e| anyhow!("Can't queue the PictureReady event {}", e))568} else {569Ok(())570}571}572573fn drain_submit_queue(&mut self) -> VideoResult<()> {574while let Some(job) = self.submit_queue.front_mut() {575let bitstream_map = BufferMapping::new(&job.resource, job.offset, job.bytes_used)576.map_err(VideoError::BackendFailure)?;577578let slice_start = job.bytes_used - job.remaining;579match self580.codec581.decode(job.timestamp, &bitstream_map.as_ref()[slice_start..])582{583Ok(processed) => {584job.remaining = job.remaining.saturating_sub(processed);585// We have completed the buffer.586if job.remaining == 0 {587// We are always done with the input buffer after decode returns.588self.event_queue589.queue_event(DecoderEvent::NotifyEndOfBitstreamBuffer(job.resource_id))590.map_err(|e| {591VideoError::BackendFailure(anyhow!(592"Can't queue the NotifyEndOfBitstream event {}",593e594))595})?;596self.submit_queue.pop_front();597}598}599Err(DecodeError::CheckEvents) => {600self.process_decoder_events()?;601break;602}603// We will succeed once buffers are returned by the client. This could be optimized604// to only retry decoding once buffers are effectively returned.605Err(DecodeError::NotEnoughOutputBuffers(_)) => break,606// TODO add an InvalidInput error to cros-codecs so we can detect these cases and607// just throw a warning instead of a fatal error?608Err(e) => {609self.event_queue610.queue_event(DecoderEvent::NotifyError(VideoError::BackendFailure(611anyhow!("Decoding buffer {} failed", job.resource_id),612)))613.map_err(|e| {614VideoError::BackendFailure(anyhow!(615"Can't queue the NotifyError event {}",616e617))618})?;619return Err(VideoError::BackendFailure(e.into()));620}621}622}623624Ok(())625}626627fn process_decoder_events(&mut self) -> VideoResult<()> {628while let Some(event) = self.codec.next_event() {629match event {630cros_codecs::decoder::DecoderEvent::FrameReady(frame) => {631Self::output_picture(frame.as_ref(), &mut self.event_queue)632.map_err(VideoError::BackendFailure)?;633let picture_id = frame.resource().picture_buffer_id;634self.held_frames635.insert(picture_id, BorrowedFrame::Decoded(frame));636}637cros_codecs::decoder::DecoderEvent::FormatChanged(mut format) => {638let coded_resolution = format.stream_info().coded_resolution;639let display_resolution = format.stream_info().display_resolution;640641// Ask the client for new buffers.642self.event_queue643.queue_event(DecoderEvent::ProvidePictureBuffers {644min_num_buffers: format.stream_info().min_num_frames as u32,645width: coded_resolution.width as i32,646height: coded_resolution.height as i32,647visible_rect: Rect {648left: 0,649top: 0,650right: display_resolution.width as i32,651bottom: display_resolution.height as i32,652},653})654.map_err(|e| VideoError::BackendFailure(e.into()))?;655656format.frame_pool().clear();657658// Drop our output queue and wait for the new number of output buffers.659self.output_queue_state = match &self.output_queue_state {660// If this is part of the initialization step, then do not switch states.661OutputQueueState::AwaitingBufferCount => {662OutputQueueState::AwaitingBufferCount663}664OutputQueueState::Decoding => OutputQueueState::Drc,665OutputQueueState::Drc => {666return Err(VideoError::BackendFailure(anyhow!(667"Invalid state during DRC."668)))669}670};671}672}673}674675Ok(())676}677678fn try_make_progress(&mut self) -> VideoResult<()> {679self.process_decoder_events()?;680self.drain_submit_queue()?;681682Ok(())683}684}685686impl DecoderSession for VaapiDecoderSession {687fn set_output_parameters(&mut self, _: usize, _: Format) -> VideoResult<()> {688let output_queue_state = &mut self.output_queue_state;689690// This logic can still be improved, in particular it needs better691// support at the virtio-video protocol level.692//693// We must ensure that set_output_parameters is only called after we are694// sure that we have processed some stream metadata, which currently is695// not the case. In particular, the {SET|GET}_PARAMS logic currently696// takes place *before* we had a chance to parse any stream metadata at697// all.698//699// This can lead to a situation where we accept a format (say, NV12),700// but then discover we are unable to decode it after processing some701// buffers (because the stream indicates that the bit depth is 10, for702// example). Note that there is no way to reject said stream as of right703// now unless we hardcode NV12 in cros-codecs itself.704//705// Nevertheless, the support is already in place in cros-codecs: the706// decoders will queue buffers until they read some metadata. At this707// point, it will allow for the negotiation of the decoded format until708// a new call to decode() is made. At the crosvm level, we can use this709// window of time to try different decoded formats with .try_format().710//711// For now, we accept the default format chosen by cros-codecs instead.712// In practice, this means NV12 if it the stream can be decoded into713// NV12 and if the hardware can do so.714715match output_queue_state {716OutputQueueState::AwaitingBufferCount | OutputQueueState::Drc => {717// Accept the default format chosen by cros-codecs instead.718//719// if let Some(backend_format) = self.backend.backend().format() {720// let backend_format = Format::try_from(backend_format);721722// let format_matches = match backend_format {723// Ok(backend_format) => backend_format != format,724// Err(_) => false,725// };726727// if !format_matches {728// let format =729// DecodedFormat::try_from(format).map_err(VideoError::BackendFailure)?;730731// self.backend.backend().try_format(format).map_err(|e| {732// VideoError::BackendFailure(anyhow!(733// "Failed to set the codec backend format: {}",734// e735// ))736// })?;737// }738// }739740*output_queue_state = OutputQueueState::Decoding;741742Ok(())743}744OutputQueueState::Decoding => {745// Covers the slightly awkward ffmpeg v4l2 stateful746// implementation for the capture queue setup.747//748// ffmpeg will queue a single OUTPUT buffer and immediately749// follow up with a VIDIOC_G_FMT call on the CAPTURE queue.750// This leads to a race condition, because it takes some751// appreciable time for the real resolution to propagate back to752// the guest as the virtio machinery processes and delivers the753// event.754//755// In the event that VIDIOC_G_FMT(capture) returns the default756// format, ffmpeg allocates buffers of the default resolution757// (640x480) only to immediately reallocate as soon as it758// processes the SRC_CH v4l2 event. Otherwise (if the resolution759// has propagated in time), this path will not be taken during760// the initialization.761//762// This leads to the following workflow in the virtio video763// worker:764// RESOURCE_QUEUE -> QUEUE_CLEAR -> RESOURCE_QUEUE765//766// Failing to accept this (as we previously did), leaves us767// with bad state and completely breaks the decoding process. We768// should replace the queue even if this is not 100% according769// to spec.770//771// On the other hand, this branch still exists to highlight the772// fact that we should assert that we have emitted a buffer with773// the LAST flag when support for buffer flags is implemented in774// a future CL. If a buffer with the LAST flag hasn't been775// emitted, it's technically a mistake to be here because we776// still have buffers of the old resolution to deliver.777*output_queue_state = OutputQueueState::Decoding;778779// TODO: check whether we have emitted a buffer with the LAST780// flag before returning.781Ok(())782}783}784}785786fn decode(787&mut self,788resource_id: u32,789timestamp: u64,790resource: GuestResourceHandle,791offset: u32,792bytes_used: u32,793) -> VideoResult<()> {794let job = PendingJob {795resource_id,796timestamp,797resource,798offset: offset as usize,799bytes_used: bytes_used as usize,800remaining: bytes_used as usize,801};802803self.submit_queue.push_back(job);804self.try_make_progress()?;805806Ok(())807}808809fn flush(&mut self) -> VideoResult<()> {810self.flushing = true;811812self.try_make_progress()?;813814if !self.submit_queue.is_empty() {815return Ok(());816}817818// Retrieve ready frames from the codec, if any.819self.codec820.flush()821.map_err(|e| VideoError::BackendFailure(e.into()))?;822self.process_decoder_events()?;823824self.try_emit_flush_completed()825.map_err(VideoError::BackendFailure)826}827828fn reset(&mut self) -> VideoResult<()> {829self.submit_queue.clear();830831// Make sure the codec is not active.832self.codec833.flush()834.map_err(|e| VideoError::BackendFailure(e.into()))?;835836self.process_decoder_events()?;837838// Drop the queued output buffers.839self.clear_output_buffers()?;840841self.event_queue842.queue_event(DecoderEvent::ResetCompleted(Ok(())))843.map_err(|e| {844VideoError::BackendFailure(anyhow!("Can't queue the ResetCompleted event {}", e))845})?;846847Ok(())848}849850fn clear_output_buffers(&mut self) -> VideoResult<()> {851// Cancel any ongoing flush.852self.flushing = false;853854// Drop all decoded frames signaled as ready and cancel any reported flush.855self.event_queue.retain(|event| {856!matches!(857event,858DecoderEvent::PictureReady { .. } | DecoderEvent::FlushCompleted(_)859)860});861862// Now hold all the imported frames until reuse_output_buffer is called on them.863let frame_pool = self.codec.frame_pool();864while let Some(frame) = frame_pool.take_free_frame() {865let picture_id = (*frame).as_ref().picture_buffer_id;866self.held_frames867.insert(picture_id, BorrowedFrame::Held(frame));868}869870Ok(())871}872873fn event_pipe(&self) -> &dyn base::AsRawDescriptor {874&self.event_queue875}876877fn use_output_buffer(878&mut self,879picture_buffer_id: i32,880resource: GuestResource,881) -> VideoResult<()> {882let output_queue_state = &mut self.output_queue_state;883if let OutputQueueState::Drc = output_queue_state {884// Reusing buffers during DRC is valid, but we won't use them and can just drop them.885return Ok(());886}887888let desc = match resource.handle {889GuestResourceHandle::GuestPages(handle) => {890BufferDescriptor::GuestMem(GuestMemDescriptor(handle))891}892GuestResourceHandle::VirtioObject(handle) => {893// SAFETY: descriptor is expected to be valid894let fd = unsafe { OwnedFd::from_raw_fd(handle.desc.into_raw_descriptor()) };895let modifier = handle.modifier;896897let frame = DmabufFrame {898fds: vec![fd],899layout: FrameLayout {900format: (cros_codecs::Fourcc::from(b"NV12"), modifier),901size: cros_codecs::Resolution::from((resource.width, resource.height)),902planes: resource903.planes904.iter()905.map(|p| PlaneLayout {906buffer_index: 0,907offset: p.offset,908stride: p.stride,909})910.collect(),911},912};913914BufferDescriptor::Dmabuf(frame)915}916};917918let desc_with_pic_id = BufferDescWithPicId {919desc,920picture_buffer_id,921};922923self.codec924.frame_pool()925.add_frames(vec![desc_with_pic_id])926.map_err(VideoError::BackendFailure)?;927928self.try_make_progress()929}930931fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()> {932let output_queue_state = &mut self.output_queue_state;933if let OutputQueueState::Drc = output_queue_state {934// Reusing buffers during DRC is valid, but we won't use them and can just drop them.935return Ok(());936}937938self.held_frames.remove(&picture_buffer_id);939940self.try_make_progress()?;941942if self.flushing {943// Try flushing again now that we have a new buffer. This might let944// us progress further in the flush operation.945self.flush()?;946}947Ok(())948}949950fn read_event(&mut self) -> VideoResult<DecoderEvent> {951self.event_queue952.dequeue_event()953.map_err(|e| VideoError::BackendFailure(anyhow!("Can't read event {}", e)))954}955}956957impl DecoderBackend for VaapiDecoder {958type Session = VaapiDecoderSession;959960fn get_capabilities(&self) -> Capability {961self.caps.clone()962}963964fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> {965let display = Display::open()966.ok_or_else(|| VideoError::BackendFailure(anyhow!("failed to open VA display")))?;967968let codec: Box<dyn StatelessVideoDecoder<BufferDescWithPicId>> = match format {969Format::VP8 => Box::new(970cros_codecs::decoder::stateless::StatelessDecoder::<Vp8, _>::new_vaapi(971display,972cros_codecs::decoder::BlockingMode::NonBlocking,973),974),975Format::VP9 => Box::new(976cros_codecs::decoder::stateless::StatelessDecoder::<Vp9, _>::new_vaapi(977display,978cros_codecs::decoder::BlockingMode::NonBlocking,979),980),981Format::H264 => Box::new(982cros_codecs::decoder::stateless::StatelessDecoder::<H264, _>::new_vaapi(983display,984cros_codecs::decoder::BlockingMode::NonBlocking,985),986),987Format::Hevc => Box::new(988cros_codecs::decoder::stateless::StatelessDecoder::<H265, _>::new_vaapi(989display,990cros_codecs::decoder::BlockingMode::NonBlocking,991),992),993_ => return Err(VideoError::InvalidFormat),994};995996Ok(VaapiDecoderSession {997codec,998output_queue_state: OutputQueueState::AwaitingBufferCount,999held_frames: Default::default(),1000submit_queue: Default::default(),1001event_queue: EventQueue::new().map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,1002flushing: Default::default(),1003})1004}1005}10061007#[cfg(test)]1008mod tests {1009use super::super::tests::*;1010use super::*;10111012#[test]1013// Ignore this test by default as it requires libva-compatible hardware.1014#[ignore]1015fn test_get_capabilities() {1016let decoder = VaapiDecoder::new().unwrap();1017let caps = decoder.get_capabilities();1018assert!(!caps.input_formats().is_empty());1019assert!(!caps.output_formats().is_empty());1020}10211022// Decode using guest memory input and output buffers.1023#[test]1024// Ignore this test by default as it requires libva-compatible hardware.1025#[ignore]1026fn test_decode_h264_guestmem_to_guestmem() {1027decode_h264_generic(1028&mut VaapiDecoder::new().unwrap(),1029build_guest_mem_handle,1030build_guest_mem_handle,1031);1032}1033}103410351036