1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2020 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker
5*bb4ee6a4SAndroid Build Coastguard Worker //! This module implements the virtio video encoder and decoder devices.
6*bb4ee6a4SAndroid Build Coastguard Worker //! The current implementation uses [v3 RFC] of the virtio-video protocol.
7*bb4ee6a4SAndroid Build Coastguard Worker //!
8*bb4ee6a4SAndroid Build Coastguard Worker //! [v3 RFC]: https://markmail.org/thread/wxdne5re7aaugbjg
9*bb4ee6a4SAndroid Build Coastguard Worker
10*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeMap;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::thread;
12*bb4ee6a4SAndroid Build Coastguard Worker
13*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::anyhow;
14*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
15*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
16*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-encoder")]
17*bb4ee6a4SAndroid Build Coastguard Worker use base::info;
18*bb4ee6a4SAndroid Build Coastguard Worker use base::AsRawDescriptor;
19*bb4ee6a4SAndroid Build Coastguard Worker use base::Error as SysError;
20*bb4ee6a4SAndroid Build Coastguard Worker use base::Event;
21*bb4ee6a4SAndroid Build Coastguard Worker use base::RawDescriptor;
22*bb4ee6a4SAndroid Build Coastguard Worker use base::Tube;
23*bb4ee6a4SAndroid Build Coastguard Worker use data_model::Le32;
24*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted;
25*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error;
26*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory;
27*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
28*bb4ee6a4SAndroid Build Coastguard Worker
29*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::copy_config;
30*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::virtio_device::VirtioDevice;
31*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::DeviceType;
32*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Interrupt;
33*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Queue;
34*bb4ee6a4SAndroid Build Coastguard Worker
35*bb4ee6a4SAndroid Build Coastguard Worker #[macro_use]
36*bb4ee6a4SAndroid Build Coastguard Worker mod macros;
37*bb4ee6a4SAndroid Build Coastguard Worker mod async_cmd_desc_map;
38*bb4ee6a4SAndroid Build Coastguard Worker mod command;
39*bb4ee6a4SAndroid Build Coastguard Worker mod control;
40*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-decoder")]
41*bb4ee6a4SAndroid Build Coastguard Worker mod decoder;
42*bb4ee6a4SAndroid Build Coastguard Worker pub mod device;
43*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-encoder")]
44*bb4ee6a4SAndroid Build Coastguard Worker mod encoder;
45*bb4ee6a4SAndroid Build Coastguard Worker mod error;
46*bb4ee6a4SAndroid Build Coastguard Worker mod event;
47*bb4ee6a4SAndroid Build Coastguard Worker mod format;
48*bb4ee6a4SAndroid Build Coastguard Worker mod params;
49*bb4ee6a4SAndroid Build Coastguard Worker mod protocol;
50*bb4ee6a4SAndroid Build Coastguard Worker mod resource;
51*bb4ee6a4SAndroid Build Coastguard Worker mod response;
52*bb4ee6a4SAndroid Build Coastguard Worker mod utils;
53*bb4ee6a4SAndroid Build Coastguard Worker pub mod worker;
54*bb4ee6a4SAndroid Build Coastguard Worker
55*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(
56*bb4ee6a4SAndroid Build Coastguard Worker feature = "video-decoder",
57*bb4ee6a4SAndroid Build Coastguard Worker not(any(feature = "libvda", feature = "ffmpeg", feature = "vaapi"))
58*bb4ee6a4SAndroid Build Coastguard Worker ))]
59*bb4ee6a4SAndroid Build Coastguard Worker compile_error!("The \"video-decoder\" feature requires at least one of \"ffmpeg\", \"libvda\" or \"vaapi\" to also be enabled.");
60*bb4ee6a4SAndroid Build Coastguard Worker
61*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(all(
62*bb4ee6a4SAndroid Build Coastguard Worker feature = "video-encoder",
63*bb4ee6a4SAndroid Build Coastguard Worker not(any(feature = "libvda", feature = "ffmpeg"))
64*bb4ee6a4SAndroid Build Coastguard Worker ))]
65*bb4ee6a4SAndroid Build Coastguard Worker compile_error!("The \"video-encoder\" feature requires at least one of \"ffmpeg\" or \"libvda\" to also be enabled.");
66*bb4ee6a4SAndroid Build Coastguard Worker
67*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "ffmpeg")]
68*bb4ee6a4SAndroid Build Coastguard Worker mod ffmpeg;
69*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "libvda")]
70*bb4ee6a4SAndroid Build Coastguard Worker mod vda;
71*bb4ee6a4SAndroid Build Coastguard Worker
72*bb4ee6a4SAndroid Build Coastguard Worker use command::ReadCmdError;
73*bb4ee6a4SAndroid Build Coastguard Worker use device::Device;
74*bb4ee6a4SAndroid Build Coastguard Worker use worker::Worker;
75*bb4ee6a4SAndroid Build Coastguard Worker
76*bb4ee6a4SAndroid Build Coastguard Worker use super::device_constants::video::backend_supported_virtio_features;
77*bb4ee6a4SAndroid Build Coastguard Worker use super::device_constants::video::virtio_video_config;
78*bb4ee6a4SAndroid Build Coastguard Worker use super::device_constants::video::VideoBackendType;
79*bb4ee6a4SAndroid Build Coastguard Worker use super::device_constants::video::VideoDeviceType;
80*bb4ee6a4SAndroid Build Coastguard Worker
81*bb4ee6a4SAndroid Build Coastguard Worker // CMD_QUEUE_SIZE = max number of command descriptors for input and output queues
82*bb4ee6a4SAndroid Build Coastguard Worker // Experimentally, it appears a stream allocates 16 input and 26 output buffers = 42 total
83*bb4ee6a4SAndroid Build Coastguard Worker // For 8 simultaneous streams, 2 descs per buffer * 42 buffers * 8 streams = 672 descs
84*bb4ee6a4SAndroid Build Coastguard Worker // Allocate 1024 to give some headroom in case of extra streams/buffers
85*bb4ee6a4SAndroid Build Coastguard Worker //
86*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/204055006): Make cmd queue size dependent of
87*bb4ee6a4SAndroid Build Coastguard Worker // (max buf cnt for input + max buf cnt for output) * max descs per buffer * max nb of streams
88*bb4ee6a4SAndroid Build Coastguard Worker const CMD_QUEUE_SIZE: u16 = 1024;
89*bb4ee6a4SAndroid Build Coastguard Worker
90*bb4ee6a4SAndroid Build Coastguard Worker // EVENT_QUEUE_SIZE = max number of event descriptors for stream events like resolution changes
91*bb4ee6a4SAndroid Build Coastguard Worker const EVENT_QUEUE_SIZE: u16 = 256;
92*bb4ee6a4SAndroid Build Coastguard Worker
93*bb4ee6a4SAndroid Build Coastguard Worker const QUEUE_SIZES: &[u16] = &[CMD_QUEUE_SIZE, EVENT_QUEUE_SIZE];
94*bb4ee6a4SAndroid Build Coastguard Worker
95*bb4ee6a4SAndroid Build Coastguard Worker /// An error indicating something went wrong in virtio-video's worker.
96*bb4ee6a4SAndroid Build Coastguard Worker #[sorted]
97*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, Debug)]
98*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error {
99*bb4ee6a4SAndroid Build Coastguard Worker /// Cloning a descriptor failed
100*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to clone a descriptor: {0}")]
101*bb4ee6a4SAndroid Build Coastguard Worker CloneDescriptorFailed(SysError),
102*bb4ee6a4SAndroid Build Coastguard Worker /// No available descriptor in which an event is written to.
103*bb4ee6a4SAndroid Build Coastguard Worker #[error("no available descriptor in which an event is written to")]
104*bb4ee6a4SAndroid Build Coastguard Worker DescriptorNotAvailable,
105*bb4ee6a4SAndroid Build Coastguard Worker /// Creating a video device failed.
106*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-decoder")]
107*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to create a video device: {0}")]
108*bb4ee6a4SAndroid Build Coastguard Worker DeviceCreationFailed(String),
109*bb4ee6a4SAndroid Build Coastguard Worker /// Making an EventAsync failed.
110*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to create an EventAsync: {0}")]
111*bb4ee6a4SAndroid Build Coastguard Worker EventAsyncCreationFailed(cros_async::AsyncError),
112*bb4ee6a4SAndroid Build Coastguard Worker /// Failed to read a virtio-video command.
113*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to read a command from the guest: {0}")]
114*bb4ee6a4SAndroid Build Coastguard Worker ReadFailure(ReadCmdError),
115*bb4ee6a4SAndroid Build Coastguard Worker /// Creating WaitContext failed.
116*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to create WaitContext: {0}")]
117*bb4ee6a4SAndroid Build Coastguard Worker WaitContextCreationFailed(SysError),
118*bb4ee6a4SAndroid Build Coastguard Worker /// Error while polling for events.
119*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to wait for events: {0}")]
120*bb4ee6a4SAndroid Build Coastguard Worker WaitError(SysError),
121*bb4ee6a4SAndroid Build Coastguard Worker /// Failed to write an event into the event queue.
122*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to write an event {event:?} into event queue: {error}")]
123*bb4ee6a4SAndroid Build Coastguard Worker WriteEventFailure {
124*bb4ee6a4SAndroid Build Coastguard Worker event: event::VideoEvt,
125*bb4ee6a4SAndroid Build Coastguard Worker error: std::io::Error,
126*bb4ee6a4SAndroid Build Coastguard Worker },
127*bb4ee6a4SAndroid Build Coastguard Worker }
128*bb4ee6a4SAndroid Build Coastguard Worker
129*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = std::result::Result<T, Error>;
130*bb4ee6a4SAndroid Build Coastguard Worker
131*bb4ee6a4SAndroid Build Coastguard Worker pub struct VideoDevice {
132*bb4ee6a4SAndroid Build Coastguard Worker device_type: VideoDeviceType,
133*bb4ee6a4SAndroid Build Coastguard Worker backend: VideoBackendType,
134*bb4ee6a4SAndroid Build Coastguard Worker kill_evt: Option<Event>,
135*bb4ee6a4SAndroid Build Coastguard Worker resource_bridge: Option<Tube>,
136*bb4ee6a4SAndroid Build Coastguard Worker base_features: u64,
137*bb4ee6a4SAndroid Build Coastguard Worker }
138*bb4ee6a4SAndroid Build Coastguard Worker
139*bb4ee6a4SAndroid Build Coastguard Worker impl VideoDevice {
new( base_features: u64, device_type: VideoDeviceType, backend: VideoBackendType, resource_bridge: Option<Tube>, ) -> VideoDevice140*bb4ee6a4SAndroid Build Coastguard Worker pub fn new(
141*bb4ee6a4SAndroid Build Coastguard Worker base_features: u64,
142*bb4ee6a4SAndroid Build Coastguard Worker device_type: VideoDeviceType,
143*bb4ee6a4SAndroid Build Coastguard Worker backend: VideoBackendType,
144*bb4ee6a4SAndroid Build Coastguard Worker resource_bridge: Option<Tube>,
145*bb4ee6a4SAndroid Build Coastguard Worker ) -> VideoDevice {
146*bb4ee6a4SAndroid Build Coastguard Worker VideoDevice {
147*bb4ee6a4SAndroid Build Coastguard Worker device_type,
148*bb4ee6a4SAndroid Build Coastguard Worker backend,
149*bb4ee6a4SAndroid Build Coastguard Worker kill_evt: None,
150*bb4ee6a4SAndroid Build Coastguard Worker resource_bridge,
151*bb4ee6a4SAndroid Build Coastguard Worker base_features,
152*bb4ee6a4SAndroid Build Coastguard Worker }
153*bb4ee6a4SAndroid Build Coastguard Worker }
154*bb4ee6a4SAndroid Build Coastguard Worker }
155*bb4ee6a4SAndroid Build Coastguard Worker
156*bb4ee6a4SAndroid Build Coastguard Worker impl Drop for VideoDevice {
drop(&mut self)157*bb4ee6a4SAndroid Build Coastguard Worker fn drop(&mut self) {
158*bb4ee6a4SAndroid Build Coastguard Worker if let Some(kill_evt) = self.kill_evt.take() {
159*bb4ee6a4SAndroid Build Coastguard Worker // Ignore the result because there is nothing we can do about it.
160*bb4ee6a4SAndroid Build Coastguard Worker let _ = kill_evt.signal();
161*bb4ee6a4SAndroid Build Coastguard Worker }
162*bb4ee6a4SAndroid Build Coastguard Worker }
163*bb4ee6a4SAndroid Build Coastguard Worker }
164*bb4ee6a4SAndroid Build Coastguard Worker
build_config(backend: VideoBackendType) -> virtio_video_config165*bb4ee6a4SAndroid Build Coastguard Worker pub fn build_config(backend: VideoBackendType) -> virtio_video_config {
166*bb4ee6a4SAndroid Build Coastguard Worker let mut device_name = [0u8; 32];
167*bb4ee6a4SAndroid Build Coastguard Worker match backend {
168*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "libvda")]
169*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Libvda => device_name[0..6].copy_from_slice("libvda".as_bytes()),
170*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "libvda")]
171*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::LibvdaVd => device_name[0..8].copy_from_slice("libvdavd".as_bytes()),
172*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "ffmpeg")]
173*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Ffmpeg => device_name[0..6].copy_from_slice("ffmpeg".as_bytes()),
174*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "vaapi")]
175*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Vaapi => device_name[0..5].copy_from_slice("vaapi".as_bytes()),
176*bb4ee6a4SAndroid Build Coastguard Worker };
177*bb4ee6a4SAndroid Build Coastguard Worker virtio_video_config {
178*bb4ee6a4SAndroid Build Coastguard Worker version: Le32::from(0),
179*bb4ee6a4SAndroid Build Coastguard Worker max_caps_length: Le32::from(1024), // Set a big number
180*bb4ee6a4SAndroid Build Coastguard Worker max_resp_length: Le32::from(1024), // Set a big number
181*bb4ee6a4SAndroid Build Coastguard Worker device_name,
182*bb4ee6a4SAndroid Build Coastguard Worker }
183*bb4ee6a4SAndroid Build Coastguard Worker }
184*bb4ee6a4SAndroid Build Coastguard Worker
185*bb4ee6a4SAndroid Build Coastguard Worker impl VirtioDevice for VideoDevice {
keep_rds(&self) -> Vec<RawDescriptor>186*bb4ee6a4SAndroid Build Coastguard Worker fn keep_rds(&self) -> Vec<RawDescriptor> {
187*bb4ee6a4SAndroid Build Coastguard Worker let mut keep_rds = Vec::new();
188*bb4ee6a4SAndroid Build Coastguard Worker if let Some(resource_bridge) = &self.resource_bridge {
189*bb4ee6a4SAndroid Build Coastguard Worker keep_rds.push(resource_bridge.as_raw_descriptor());
190*bb4ee6a4SAndroid Build Coastguard Worker }
191*bb4ee6a4SAndroid Build Coastguard Worker keep_rds
192*bb4ee6a4SAndroid Build Coastguard Worker }
193*bb4ee6a4SAndroid Build Coastguard Worker
device_type(&self) -> DeviceType194*bb4ee6a4SAndroid Build Coastguard Worker fn device_type(&self) -> DeviceType {
195*bb4ee6a4SAndroid Build Coastguard Worker match &self.device_type {
196*bb4ee6a4SAndroid Build Coastguard Worker VideoDeviceType::Decoder => DeviceType::VideoDecoder,
197*bb4ee6a4SAndroid Build Coastguard Worker VideoDeviceType::Encoder => DeviceType::VideoEncoder,
198*bb4ee6a4SAndroid Build Coastguard Worker }
199*bb4ee6a4SAndroid Build Coastguard Worker }
200*bb4ee6a4SAndroid Build Coastguard Worker
queue_max_sizes(&self) -> &[u16]201*bb4ee6a4SAndroid Build Coastguard Worker fn queue_max_sizes(&self) -> &[u16] {
202*bb4ee6a4SAndroid Build Coastguard Worker QUEUE_SIZES
203*bb4ee6a4SAndroid Build Coastguard Worker }
204*bb4ee6a4SAndroid Build Coastguard Worker
features(&self) -> u64205*bb4ee6a4SAndroid Build Coastguard Worker fn features(&self) -> u64 {
206*bb4ee6a4SAndroid Build Coastguard Worker self.base_features | backend_supported_virtio_features(self.backend)
207*bb4ee6a4SAndroid Build Coastguard Worker }
208*bb4ee6a4SAndroid Build Coastguard Worker
read_config(&self, offset: u64, data: &mut [u8])209*bb4ee6a4SAndroid Build Coastguard Worker fn read_config(&self, offset: u64, data: &mut [u8]) {
210*bb4ee6a4SAndroid Build Coastguard Worker let mut cfg = build_config(self.backend);
211*bb4ee6a4SAndroid Build Coastguard Worker copy_config(data, 0, cfg.as_bytes_mut(), offset);
212*bb4ee6a4SAndroid Build Coastguard Worker }
213*bb4ee6a4SAndroid Build Coastguard Worker
activate( &mut self, mem: GuestMemory, _interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>214*bb4ee6a4SAndroid Build Coastguard Worker fn activate(
215*bb4ee6a4SAndroid Build Coastguard Worker &mut self,
216*bb4ee6a4SAndroid Build Coastguard Worker mem: GuestMemory,
217*bb4ee6a4SAndroid Build Coastguard Worker _interrupt: Interrupt,
218*bb4ee6a4SAndroid Build Coastguard Worker mut queues: BTreeMap<usize, Queue>,
219*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<()> {
220*bb4ee6a4SAndroid Build Coastguard Worker if queues.len() != QUEUE_SIZES.len() {
221*bb4ee6a4SAndroid Build Coastguard Worker return Err(anyhow!(
222*bb4ee6a4SAndroid Build Coastguard Worker "wrong number of queues are passed: expected {}, actual {}",
223*bb4ee6a4SAndroid Build Coastguard Worker queues.len(),
224*bb4ee6a4SAndroid Build Coastguard Worker QUEUE_SIZES.len()
225*bb4ee6a4SAndroid Build Coastguard Worker ));
226*bb4ee6a4SAndroid Build Coastguard Worker }
227*bb4ee6a4SAndroid Build Coastguard Worker
228*bb4ee6a4SAndroid Build Coastguard Worker let (self_kill_evt, kill_evt) = Event::new()
229*bb4ee6a4SAndroid Build Coastguard Worker .and_then(|e| Ok((e.try_clone()?, e)))
230*bb4ee6a4SAndroid Build Coastguard Worker .context("failed to create kill Event pair")?;
231*bb4ee6a4SAndroid Build Coastguard Worker self.kill_evt = Some(self_kill_evt);
232*bb4ee6a4SAndroid Build Coastguard Worker
233*bb4ee6a4SAndroid Build Coastguard Worker let cmd_queue = queues.pop_first().unwrap().1;
234*bb4ee6a4SAndroid Build Coastguard Worker let event_queue = queues.pop_first().unwrap().1;
235*bb4ee6a4SAndroid Build Coastguard Worker let backend = self.backend;
236*bb4ee6a4SAndroid Build Coastguard Worker let resource_bridge = self
237*bb4ee6a4SAndroid Build Coastguard Worker .resource_bridge
238*bb4ee6a4SAndroid Build Coastguard Worker .take()
239*bb4ee6a4SAndroid Build Coastguard Worker .context("no resource bridge is passed")?;
240*bb4ee6a4SAndroid Build Coastguard Worker let mut worker = Worker::new(cmd_queue, event_queue);
241*bb4ee6a4SAndroid Build Coastguard Worker
242*bb4ee6a4SAndroid Build Coastguard Worker let worker_result = match &self.device_type {
243*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-decoder")]
244*bb4ee6a4SAndroid Build Coastguard Worker VideoDeviceType::Decoder => thread::Builder::new()
245*bb4ee6a4SAndroid Build Coastguard Worker .name("v_video_decoder".to_owned())
246*bb4ee6a4SAndroid Build Coastguard Worker .spawn(move || {
247*bb4ee6a4SAndroid Build Coastguard Worker let device: Box<dyn Device> =
248*bb4ee6a4SAndroid Build Coastguard Worker match create_decoder_device(backend, resource_bridge, mem) {
249*bb4ee6a4SAndroid Build Coastguard Worker Ok(value) => value,
250*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
251*bb4ee6a4SAndroid Build Coastguard Worker error!("{}", e);
252*bb4ee6a4SAndroid Build Coastguard Worker return;
253*bb4ee6a4SAndroid Build Coastguard Worker }
254*bb4ee6a4SAndroid Build Coastguard Worker };
255*bb4ee6a4SAndroid Build Coastguard Worker
256*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = worker.run(device, &kill_evt) {
257*bb4ee6a4SAndroid Build Coastguard Worker error!("Failed to start decoder worker: {}", e);
258*bb4ee6a4SAndroid Build Coastguard Worker };
259*bb4ee6a4SAndroid Build Coastguard Worker // Don't return any information since the return value is never checked.
260*bb4ee6a4SAndroid Build Coastguard Worker }),
261*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-encoder")]
262*bb4ee6a4SAndroid Build Coastguard Worker VideoDeviceType::Encoder => thread::Builder::new()
263*bb4ee6a4SAndroid Build Coastguard Worker .name("v_video_encoder".to_owned())
264*bb4ee6a4SAndroid Build Coastguard Worker .spawn(move || {
265*bb4ee6a4SAndroid Build Coastguard Worker let device: Box<dyn Device> = match backend {
266*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "libvda")]
267*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Libvda => {
268*bb4ee6a4SAndroid Build Coastguard Worker let vda = match encoder::backend::vda::LibvdaEncoder::new() {
269*bb4ee6a4SAndroid Build Coastguard Worker Ok(vda) => vda,
270*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
271*bb4ee6a4SAndroid Build Coastguard Worker error!("Failed to initialize VDA for encoder: {}", e);
272*bb4ee6a4SAndroid Build Coastguard Worker return;
273*bb4ee6a4SAndroid Build Coastguard Worker }
274*bb4ee6a4SAndroid Build Coastguard Worker };
275*bb4ee6a4SAndroid Build Coastguard Worker
276*bb4ee6a4SAndroid Build Coastguard Worker match encoder::EncoderDevice::new(vda, resource_bridge, mem) {
277*bb4ee6a4SAndroid Build Coastguard Worker Ok(encoder) => Box::new(encoder),
278*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
279*bb4ee6a4SAndroid Build Coastguard Worker error!("Failed to create encoder device: {}", e);
280*bb4ee6a4SAndroid Build Coastguard Worker return;
281*bb4ee6a4SAndroid Build Coastguard Worker }
282*bb4ee6a4SAndroid Build Coastguard Worker }
283*bb4ee6a4SAndroid Build Coastguard Worker }
284*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "libvda")]
285*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::LibvdaVd => {
286*bb4ee6a4SAndroid Build Coastguard Worker error!("Invalid backend for encoder");
287*bb4ee6a4SAndroid Build Coastguard Worker return;
288*bb4ee6a4SAndroid Build Coastguard Worker }
289*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "ffmpeg")]
290*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Ffmpeg => {
291*bb4ee6a4SAndroid Build Coastguard Worker let ffmpeg = encoder::backend::ffmpeg::FfmpegEncoder::new();
292*bb4ee6a4SAndroid Build Coastguard Worker
293*bb4ee6a4SAndroid Build Coastguard Worker match encoder::EncoderDevice::new(ffmpeg, resource_bridge, mem) {
294*bb4ee6a4SAndroid Build Coastguard Worker Ok(encoder) => Box::new(encoder),
295*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
296*bb4ee6a4SAndroid Build Coastguard Worker error!("Failed to create encoder device: {}", e);
297*bb4ee6a4SAndroid Build Coastguard Worker return;
298*bb4ee6a4SAndroid Build Coastguard Worker }
299*bb4ee6a4SAndroid Build Coastguard Worker }
300*bb4ee6a4SAndroid Build Coastguard Worker }
301*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "vaapi")]
302*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Vaapi => {
303*bb4ee6a4SAndroid Build Coastguard Worker error!("The VA-API encoder is not supported yet");
304*bb4ee6a4SAndroid Build Coastguard Worker return;
305*bb4ee6a4SAndroid Build Coastguard Worker }
306*bb4ee6a4SAndroid Build Coastguard Worker };
307*bb4ee6a4SAndroid Build Coastguard Worker
308*bb4ee6a4SAndroid Build Coastguard Worker if let Err(e) = worker.run(device, &kill_evt) {
309*bb4ee6a4SAndroid Build Coastguard Worker error!("Failed to start encoder worker: {}", e);
310*bb4ee6a4SAndroid Build Coastguard Worker }
311*bb4ee6a4SAndroid Build Coastguard Worker }),
312*bb4ee6a4SAndroid Build Coastguard Worker #[allow(unreachable_patterns)]
313*bb4ee6a4SAndroid Build Coastguard Worker // A device will never be created for a device type not enabled
314*bb4ee6a4SAndroid Build Coastguard Worker device_type => unreachable!("Not compiled with {:?} enabled", device_type),
315*bb4ee6a4SAndroid Build Coastguard Worker };
316*bb4ee6a4SAndroid Build Coastguard Worker worker_result.with_context(|| {
317*bb4ee6a4SAndroid Build Coastguard Worker format!(
318*bb4ee6a4SAndroid Build Coastguard Worker "failed to spawn virtio_video worker for {:?}",
319*bb4ee6a4SAndroid Build Coastguard Worker &self.device_type
320*bb4ee6a4SAndroid Build Coastguard Worker )
321*bb4ee6a4SAndroid Build Coastguard Worker })?;
322*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
323*bb4ee6a4SAndroid Build Coastguard Worker }
324*bb4ee6a4SAndroid Build Coastguard Worker }
325*bb4ee6a4SAndroid Build Coastguard Worker
326*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-decoder")]
create_decoder_device( backend: VideoBackendType, resource_bridge: Tube, mem: GuestMemory, ) -> Result<Box<dyn Device>>327*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_decoder_device(
328*bb4ee6a4SAndroid Build Coastguard Worker backend: VideoBackendType,
329*bb4ee6a4SAndroid Build Coastguard Worker resource_bridge: Tube,
330*bb4ee6a4SAndroid Build Coastguard Worker mem: GuestMemory,
331*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<Box<dyn Device>> {
332*bb4ee6a4SAndroid Build Coastguard Worker use decoder::backend::DecoderBackend;
333*bb4ee6a4SAndroid Build Coastguard Worker
334*bb4ee6a4SAndroid Build Coastguard Worker let backend = match backend {
335*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "libvda")]
336*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Libvda => {
337*bb4ee6a4SAndroid Build Coastguard Worker decoder::backend::vda::LibvdaDecoder::new(libvda::decode::VdaImplType::Gavda)
338*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| {
339*bb4ee6a4SAndroid Build Coastguard Worker Error::DeviceCreationFailed(format!(
340*bb4ee6a4SAndroid Build Coastguard Worker "Failed to initialize VDA for decoder: {}",
341*bb4ee6a4SAndroid Build Coastguard Worker e
342*bb4ee6a4SAndroid Build Coastguard Worker ))
343*bb4ee6a4SAndroid Build Coastguard Worker })?
344*bb4ee6a4SAndroid Build Coastguard Worker .into_trait_object()
345*bb4ee6a4SAndroid Build Coastguard Worker }
346*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "libvda")]
347*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::LibvdaVd => {
348*bb4ee6a4SAndroid Build Coastguard Worker decoder::backend::vda::LibvdaDecoder::new(libvda::decode::VdaImplType::Gavd)
349*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| {
350*bb4ee6a4SAndroid Build Coastguard Worker Error::DeviceCreationFailed(format!(
351*bb4ee6a4SAndroid Build Coastguard Worker "Failed to initialize VD for decoder: {}",
352*bb4ee6a4SAndroid Build Coastguard Worker e
353*bb4ee6a4SAndroid Build Coastguard Worker ))
354*bb4ee6a4SAndroid Build Coastguard Worker })?
355*bb4ee6a4SAndroid Build Coastguard Worker .into_trait_object()
356*bb4ee6a4SAndroid Build Coastguard Worker }
357*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "ffmpeg")]
358*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Ffmpeg => {
359*bb4ee6a4SAndroid Build Coastguard Worker decoder::backend::ffmpeg::FfmpegDecoder::new().into_trait_object()
360*bb4ee6a4SAndroid Build Coastguard Worker }
361*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "vaapi")]
362*bb4ee6a4SAndroid Build Coastguard Worker VideoBackendType::Vaapi => decoder::backend::vaapi::VaapiDecoder::new()
363*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| {
364*bb4ee6a4SAndroid Build Coastguard Worker Error::DeviceCreationFailed(format!(
365*bb4ee6a4SAndroid Build Coastguard Worker "Failed to initialize VA-API driver for decoder: {}",
366*bb4ee6a4SAndroid Build Coastguard Worker e
367*bb4ee6a4SAndroid Build Coastguard Worker ))
368*bb4ee6a4SAndroid Build Coastguard Worker })?
369*bb4ee6a4SAndroid Build Coastguard Worker .into_trait_object(),
370*bb4ee6a4SAndroid Build Coastguard Worker };
371*bb4ee6a4SAndroid Build Coastguard Worker
372*bb4ee6a4SAndroid Build Coastguard Worker Ok(Box::new(decoder::Decoder::new(
373*bb4ee6a4SAndroid Build Coastguard Worker backend,
374*bb4ee6a4SAndroid Build Coastguard Worker resource_bridge,
375*bb4ee6a4SAndroid Build Coastguard Worker mem,
376*bb4ee6a4SAndroid Build Coastguard Worker )))
377*bb4ee6a4SAndroid Build Coastguard Worker }
378*bb4ee6a4SAndroid Build Coastguard Worker
379*bb4ee6a4SAndroid Build Coastguard Worker /// Manages the zero-length, EOS-marked buffer signaling the end of a stream.
380*bb4ee6a4SAndroid Build Coastguard Worker ///
381*bb4ee6a4SAndroid Build Coastguard Worker /// Both the decoder and encoder need to signal end-of-stream events using a zero-sized buffer
382*bb4ee6a4SAndroid Build Coastguard Worker /// marked with the `VIRTIO_VIDEO_BUFFER_FLAG_EOS` flag. This struct allows to keep a buffer aside
383*bb4ee6a4SAndroid Build Coastguard Worker /// for that purpose.
384*bb4ee6a4SAndroid Build Coastguard Worker ///
385*bb4ee6a4SAndroid Build Coastguard Worker /// TODO(b/149725148): Remove this when libvda supports buffer flags.
386*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-encoder")]
387*bb4ee6a4SAndroid Build Coastguard Worker struct EosBufferManager {
388*bb4ee6a4SAndroid Build Coastguard Worker stream_id: u32,
389*bb4ee6a4SAndroid Build Coastguard Worker eos_buffer: Option<u32>,
390*bb4ee6a4SAndroid Build Coastguard Worker client_awaits_eos: bool,
391*bb4ee6a4SAndroid Build Coastguard Worker responses: Vec<device::VideoEvtResponseType>,
392*bb4ee6a4SAndroid Build Coastguard Worker }
393*bb4ee6a4SAndroid Build Coastguard Worker
394*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "video-encoder")]
395*bb4ee6a4SAndroid Build Coastguard Worker impl EosBufferManager {
396*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new EOS manager for stream `stream_id`.
new(stream_id: u32) -> Self397*bb4ee6a4SAndroid Build Coastguard Worker fn new(stream_id: u32) -> Self {
398*bb4ee6a4SAndroid Build Coastguard Worker Self {
399*bb4ee6a4SAndroid Build Coastguard Worker stream_id,
400*bb4ee6a4SAndroid Build Coastguard Worker eos_buffer: None,
401*bb4ee6a4SAndroid Build Coastguard Worker client_awaits_eos: false,
402*bb4ee6a4SAndroid Build Coastguard Worker responses: Default::default(),
403*bb4ee6a4SAndroid Build Coastguard Worker }
404*bb4ee6a4SAndroid Build Coastguard Worker }
405*bb4ee6a4SAndroid Build Coastguard Worker
406*bb4ee6a4SAndroid Build Coastguard Worker /// Attempt to reserve buffer `buffer_id` for use as EOS buffer.
407*bb4ee6a4SAndroid Build Coastguard Worker ///
408*bb4ee6a4SAndroid Build Coastguard Worker /// This method should be called by the output buffer queueing code of the device. It returns
409*bb4ee6a4SAndroid Build Coastguard Worker /// `true` if the buffer has been kept aside for EOS, `false` otherwise (which means another
410*bb4ee6a4SAndroid Build Coastguard Worker /// buffer is already kept aside for EOS). If `true` is returned, the client must not use the
411*bb4ee6a4SAndroid Build Coastguard Worker /// buffer for any other purpose.
try_reserve_eos_buffer(&mut self, buffer_id: u32) -> bool412*bb4ee6a4SAndroid Build Coastguard Worker fn try_reserve_eos_buffer(&mut self, buffer_id: u32) -> bool {
413*bb4ee6a4SAndroid Build Coastguard Worker let is_none = self.eos_buffer.is_none();
414*bb4ee6a4SAndroid Build Coastguard Worker
415*bb4ee6a4SAndroid Build Coastguard Worker if is_none {
416*bb4ee6a4SAndroid Build Coastguard Worker info!(
417*bb4ee6a4SAndroid Build Coastguard Worker "stream {}: keeping buffer {} aside to signal EOS.",
418*bb4ee6a4SAndroid Build Coastguard Worker self.stream_id, buffer_id
419*bb4ee6a4SAndroid Build Coastguard Worker );
420*bb4ee6a4SAndroid Build Coastguard Worker self.eos_buffer = Some(buffer_id);
421*bb4ee6a4SAndroid Build Coastguard Worker }
422*bb4ee6a4SAndroid Build Coastguard Worker
423*bb4ee6a4SAndroid Build Coastguard Worker is_none
424*bb4ee6a4SAndroid Build Coastguard Worker }
425*bb4ee6a4SAndroid Build Coastguard Worker
426*bb4ee6a4SAndroid Build Coastguard Worker /// Attempt to complete an EOS event using the previously reserved buffer, if available.
427*bb4ee6a4SAndroid Build Coastguard Worker ///
428*bb4ee6a4SAndroid Build Coastguard Worker /// `responses` is a vector of responses to be sent to the driver along with the EOS buffer. If
429*bb4ee6a4SAndroid Build Coastguard Worker /// an EOS buffer has been made available using the `try_reserve_eos_buffer` method, then this
430*bb4ee6a4SAndroid Build Coastguard Worker /// method returns the `responses` vector with the EOS buffer dequeue appended in first
431*bb4ee6a4SAndroid Build Coastguard Worker /// position.
432*bb4ee6a4SAndroid Build Coastguard Worker ///
433*bb4ee6a4SAndroid Build Coastguard Worker /// If no EOS buffer is available, then the contents of `responses` is put aside, and will be
434*bb4ee6a4SAndroid Build Coastguard Worker /// returned the next time this method is called with an EOS buffer available. When this
435*bb4ee6a4SAndroid Build Coastguard Worker /// happens, `client_awaits_eos` will be set to true, and the client can check this member and
436*bb4ee6a4SAndroid Build Coastguard Worker /// call this method again right after queuing the next buffer to obtain the EOS response as
437*bb4ee6a4SAndroid Build Coastguard Worker /// soon as is possible.
try_complete_eos( &mut self, responses: Vec<device::VideoEvtResponseType>, ) -> Option<Vec<device::VideoEvtResponseType>>438*bb4ee6a4SAndroid Build Coastguard Worker fn try_complete_eos(
439*bb4ee6a4SAndroid Build Coastguard Worker &mut self,
440*bb4ee6a4SAndroid Build Coastguard Worker responses: Vec<device::VideoEvtResponseType>,
441*bb4ee6a4SAndroid Build Coastguard Worker ) -> Option<Vec<device::VideoEvtResponseType>> {
442*bb4ee6a4SAndroid Build Coastguard Worker let eos_buffer_id = self.eos_buffer.take().or_else(|| {
443*bb4ee6a4SAndroid Build Coastguard Worker info!("stream {}: no EOS resource available on successful flush response, waiting for next buffer to be queued.", self.stream_id);
444*bb4ee6a4SAndroid Build Coastguard Worker self.client_awaits_eos = true;
445*bb4ee6a4SAndroid Build Coastguard Worker if !self.responses.is_empty() {
446*bb4ee6a4SAndroid Build Coastguard Worker error!("stream {}: EOS requested while one is already in progress. This is a bug!", self.stream_id);
447*bb4ee6a4SAndroid Build Coastguard Worker }
448*bb4ee6a4SAndroid Build Coastguard Worker self.responses = responses;
449*bb4ee6a4SAndroid Build Coastguard Worker None
450*bb4ee6a4SAndroid Build Coastguard Worker })?;
451*bb4ee6a4SAndroid Build Coastguard Worker
452*bb4ee6a4SAndroid Build Coastguard Worker let eos_tag = device::AsyncCmdTag::Queue {
453*bb4ee6a4SAndroid Build Coastguard Worker stream_id: self.stream_id,
454*bb4ee6a4SAndroid Build Coastguard Worker queue_type: command::QueueType::Output,
455*bb4ee6a4SAndroid Build Coastguard Worker resource_id: eos_buffer_id,
456*bb4ee6a4SAndroid Build Coastguard Worker };
457*bb4ee6a4SAndroid Build Coastguard Worker
458*bb4ee6a4SAndroid Build Coastguard Worker let eos_response = response::CmdResponse::ResourceQueue {
459*bb4ee6a4SAndroid Build Coastguard Worker timestamp: 0,
460*bb4ee6a4SAndroid Build Coastguard Worker flags: protocol::VIRTIO_VIDEO_BUFFER_FLAG_EOS,
461*bb4ee6a4SAndroid Build Coastguard Worker size: 0,
462*bb4ee6a4SAndroid Build Coastguard Worker };
463*bb4ee6a4SAndroid Build Coastguard Worker
464*bb4ee6a4SAndroid Build Coastguard Worker self.client_awaits_eos = false;
465*bb4ee6a4SAndroid Build Coastguard Worker
466*bb4ee6a4SAndroid Build Coastguard Worker info!(
467*bb4ee6a4SAndroid Build Coastguard Worker "stream {}: signaling EOS using buffer {}.",
468*bb4ee6a4SAndroid Build Coastguard Worker self.stream_id, eos_buffer_id
469*bb4ee6a4SAndroid Build Coastguard Worker );
470*bb4ee6a4SAndroid Build Coastguard Worker
471*bb4ee6a4SAndroid Build Coastguard Worker let mut responses = std::mem::take(&mut self.responses);
472*bb4ee6a4SAndroid Build Coastguard Worker responses.insert(
473*bb4ee6a4SAndroid Build Coastguard Worker 0,
474*bb4ee6a4SAndroid Build Coastguard Worker device::VideoEvtResponseType::AsyncCmd(device::AsyncCmdResponse::from_response(
475*bb4ee6a4SAndroid Build Coastguard Worker eos_tag,
476*bb4ee6a4SAndroid Build Coastguard Worker eos_response,
477*bb4ee6a4SAndroid Build Coastguard Worker )),
478*bb4ee6a4SAndroid Build Coastguard Worker );
479*bb4ee6a4SAndroid Build Coastguard Worker
480*bb4ee6a4SAndroid Build Coastguard Worker Some(responses)
481*bb4ee6a4SAndroid Build Coastguard Worker }
482*bb4ee6a4SAndroid Build Coastguard Worker
483*bb4ee6a4SAndroid Build Coastguard Worker /// Reset the state of the manager, for use during e.g. stream resets.
reset(&mut self)484*bb4ee6a4SAndroid Build Coastguard Worker fn reset(&mut self) {
485*bb4ee6a4SAndroid Build Coastguard Worker self.eos_buffer = None;
486*bb4ee6a4SAndroid Build Coastguard Worker self.client_awaits_eos = false;
487*bb4ee6a4SAndroid Build Coastguard Worker self.responses.clear();
488*bb4ee6a4SAndroid Build Coastguard Worker }
489*bb4ee6a4SAndroid Build Coastguard Worker }
490