xref: /aosp_15_r20/external/crosvm/devices/src/virtio/video/encoder/backend/vda.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2021 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::collections::btree_map::Entry;
6 use std::collections::BTreeMap;
7 
8 use anyhow::anyhow;
9 use anyhow::Context;
10 use base::error;
11 use base::warn;
12 use base::AsRawDescriptor;
13 use base::IntoRawDescriptor;
14 use libvda::encode::EncodeCapabilities;
15 use libvda::encode::VeaImplType;
16 use libvda::encode::VeaInstance;
17 
18 use super::*;
19 use crate::virtio::video::encoder::*;
20 use crate::virtio::video::error::VideoError;
21 use crate::virtio::video::error::VideoResult;
22 use crate::virtio::video::format::Bitrate;
23 use crate::virtio::video::format::Format;
24 use crate::virtio::video::format::FormatDesc;
25 use crate::virtio::video::format::FormatRange;
26 use crate::virtio::video::format::FrameFormat;
27 use crate::virtio::video::format::Level;
28 use crate::virtio::video::format::Profile;
29 use crate::virtio::video::resource::GuestResource;
30 use crate::virtio::video::resource::GuestResourceHandle;
31 
32 impl From<Bitrate> for libvda::encode::Bitrate {
from(bitrate: Bitrate) -> Self33     fn from(bitrate: Bitrate) -> Self {
34         libvda::encode::Bitrate {
35             mode: match bitrate {
36                 Bitrate::Vbr { .. } => libvda::encode::BitrateMode::VBR,
37                 Bitrate::Cbr { .. } => libvda::encode::BitrateMode::CBR,
38             },
39             target: bitrate.target(),
40             peak: match &bitrate {
41                 // No need to specify peak if mode is CBR.
42                 Bitrate::Cbr { .. } => 0,
43                 Bitrate::Vbr { peak, .. } => *peak,
44             },
45         }
46     }
47 }
48 
49 /// A VDA encoder backend that can be passed to `EncoderDevice::new` in order to create a working
50 /// encoder.
51 pub struct LibvdaEncoder {
52     instance: VeaInstance,
53     capabilities: EncoderCapabilities,
54 }
55 
56 impl LibvdaEncoder {
new() -> VideoResult<Self>57     pub fn new() -> VideoResult<Self> {
58         let instance = VeaInstance::new(VeaImplType::Gavea)?;
59 
60         let EncodeCapabilities {
61             input_formats,
62             output_formats,
63         } = instance.get_capabilities();
64 
65         if input_formats.is_empty() || output_formats.is_empty() {
66             error!("No input or output formats.");
67             return Err(VideoError::InvalidFormat);
68         }
69 
70         let input_format_descs: Vec<FormatDesc> = input_formats
71             .iter()
72             .map(|input_format| {
73                 let format = match input_format {
74                     libvda::PixelFormat::NV12 => Format::NV12,
75                     libvda::PixelFormat::YV12 => Format::YUV420,
76                 };
77 
78                 // VEA's GetSupportedProfiles does not return resolution information.
79                 // The input formats are retrieved by querying minigbm.
80                 // TODO(alexlau): Populate this with real information.
81 
82                 FormatDesc {
83                     mask: !(u64::MAX << output_formats.len()),
84                     format,
85                     frame_formats: vec![FrameFormat {
86                         width: FormatRange {
87                             min: 2,
88                             max: 4096,
89                             step: 1,
90                         },
91                         height: FormatRange {
92                             min: 2,
93                             max: 4096,
94                             step: 1,
95                         },
96                         bitrates: vec![FormatRange {
97                             min: 0,
98                             max: 8000,
99                             step: 1,
100                         }],
101                     }],
102                     plane_align: 1,
103                 }
104             })
105             .collect();
106 
107         if !input_format_descs
108             .iter()
109             .any(|fd| fd.format == Format::NV12)
110         {
111             // NV12 is currently the only supported pixel format for libvda.
112             error!("libvda encoder does not support NV12.");
113             return Err(VideoError::InvalidFormat);
114         }
115 
116         struct ParsedFormat {
117             profiles: Vec<Profile>,
118             max_width: u32,
119             max_height: u32,
120         }
121         let mut parsed_formats: BTreeMap<Format, ParsedFormat> = BTreeMap::new();
122 
123         for output_format in output_formats.iter() {
124             // TODO(alexlau): Consider using `max_framerate_numerator` and
125             // `max_framerate_denominator`.
126             let libvda::encode::OutputProfile {
127                 profile: libvda_profile,
128                 max_width,
129                 max_height,
130                 ..
131             } = output_format;
132 
133             let profile = match Profile::from_libvda_profile(*libvda_profile) {
134                 Some(p) => p,
135                 None => {
136                     warn!("Skipping unsupported libvda profile: {:?}", libvda_profile);
137                     continue;
138                 }
139             };
140 
141             match parsed_formats.entry(profile.to_format()) {
142                 Entry::Occupied(mut occupied_entry) => {
143                     let parsed_format = occupied_entry.get_mut();
144                     parsed_format.profiles.push(profile);
145                     // If we get different libvda profiles of the same VIRTIO_VIDEO_FORMAT
146                     // (Format) that have different max resolutions or bitrates, take the
147                     // minimum between all of the different profiles.
148                     parsed_format.max_width = std::cmp::min(*max_width, parsed_format.max_width);
149                     parsed_format.max_height = std::cmp::min(*max_height, parsed_format.max_height);
150                 }
151                 Entry::Vacant(vacant_entry) => {
152                     vacant_entry.insert(ParsedFormat {
153                         profiles: vec![profile],
154                         max_width: *max_width,
155                         max_height: *max_height,
156                     });
157                 }
158             }
159         }
160 
161         let mut output_format_descs = vec![];
162         let mut coded_format_profiles = BTreeMap::new();
163         for (format, parsed_format) in parsed_formats.into_iter() {
164             let ParsedFormat {
165                 mut profiles,
166                 max_width,
167                 max_height,
168             } = parsed_format;
169 
170             output_format_descs.push(FormatDesc {
171                 mask: !(u64::MAX << output_formats.len()),
172                 format,
173                 frame_formats: vec![FrameFormat {
174                     width: FormatRange {
175                         min: 2,
176                         max: max_width,
177                         step: 1,
178                     },
179                     height: FormatRange {
180                         min: 2,
181                         max: max_height,
182                         step: 1,
183                     },
184                     bitrates: vec![FormatRange {
185                         min: 0,
186                         max: 8000,
187                         step: 1,
188                     }],
189                 }],
190                 plane_align: 1,
191             });
192 
193             profiles.sort_unstable();
194             coded_format_profiles.insert(format, profiles);
195         }
196 
197         Ok(LibvdaEncoder {
198             instance,
199             capabilities: EncoderCapabilities {
200                 input_format_descs,
201                 output_format_descs,
202                 coded_format_profiles,
203             },
204         })
205     }
206 }
207 
208 impl Encoder for LibvdaEncoder {
209     type Session = LibvdaEncoderSession;
210 
query_capabilities(&self) -> VideoResult<EncoderCapabilities>211     fn query_capabilities(&self) -> VideoResult<EncoderCapabilities> {
212         Ok(self.capabilities.clone())
213     }
214 
start_session(&mut self, config: SessionConfig) -> VideoResult<LibvdaEncoderSession>215     fn start_session(&mut self, config: SessionConfig) -> VideoResult<LibvdaEncoderSession> {
216         if config.dst_params.format.is_none() {
217             return Err(VideoError::InvalidArgument);
218         }
219 
220         let input_format = match config
221             .src_params
222             .format
223             .ok_or(VideoError::InvalidArgument)?
224         {
225             Format::NV12 => libvda::PixelFormat::NV12,
226             Format::YUV420 => libvda::PixelFormat::YV12,
227             unsupported_format => {
228                 error!("Unsupported libvda format: {}", unsupported_format);
229                 return Err(VideoError::InvalidArgument);
230             }
231         };
232 
233         let output_profile = match config.dst_profile.to_libvda_profile() {
234             Some(p) => p,
235             None => {
236                 error!("Unsupported libvda profile");
237                 return Err(VideoError::InvalidArgument);
238             }
239         };
240 
241         let config = libvda::encode::Config {
242             input_format,
243             input_visible_width: config.src_params.frame_width,
244             input_visible_height: config.src_params.frame_height,
245             output_profile,
246             bitrate: config.dst_bitrate.into(),
247             initial_framerate: if config.frame_rate == 0 {
248                 None
249             } else {
250                 Some(config.frame_rate)
251             },
252             h264_output_level: config.dst_h264_level.map(|level| {
253                 // This value is aligned to the H264 standard definition of SPS.level_idc.
254                 match level {
255                     Level::H264_1_0 => 10,
256                     Level::H264_1_1 => 11,
257                     Level::H264_1_2 => 12,
258                     Level::H264_1_3 => 13,
259                     Level::H264_2_0 => 20,
260                     Level::H264_2_1 => 21,
261                     Level::H264_2_2 => 22,
262                     Level::H264_3_0 => 30,
263                     Level::H264_3_1 => 31,
264                     Level::H264_3_2 => 32,
265                     Level::H264_4_0 => 40,
266                     Level::H264_4_1 => 41,
267                     Level::H264_4_2 => 42,
268                     Level::H264_5_0 => 50,
269                     Level::H264_5_1 => 51,
270                 }
271             }),
272         };
273 
274         let session = self.instance.open_session(config)?;
275 
276         Ok(LibvdaEncoderSession {
277             session,
278             next_input_buffer_id: 1,
279             next_output_buffer_id: 1,
280         })
281     }
282 
stop_session(&mut self, _session: LibvdaEncoderSession) -> VideoResult<()>283     fn stop_session(&mut self, _session: LibvdaEncoderSession) -> VideoResult<()> {
284         // Resources will be freed when `_session` is dropped.
285         Ok(())
286     }
287 }
288 
289 pub struct LibvdaEncoderSession {
290     session: libvda::encode::Session,
291     next_input_buffer_id: InputBufferId,
292     next_output_buffer_id: OutputBufferId,
293 }
294 
295 impl EncoderSession for LibvdaEncoderSession {
encode( &mut self, resource: GuestResource, timestamp: u64, force_keyframe: bool, ) -> VideoResult<InputBufferId>296     fn encode(
297         &mut self,
298         resource: GuestResource,
299         timestamp: u64,
300         force_keyframe: bool,
301     ) -> VideoResult<InputBufferId> {
302         let input_buffer_id = self.next_input_buffer_id;
303         let desc = match resource.handle {
304             GuestResourceHandle::VirtioObject(handle) => handle.desc,
305             _ => {
306                 return Err(VideoError::BackendFailure(anyhow!(
307                     "VDA backend only supports virtio object resources"
308                 )))
309             }
310         };
311 
312         let libvda_planes = resource
313             .planes
314             .iter()
315             .map(|plane| libvda::FramePlane {
316                 offset: plane.offset as i32,
317                 stride: plane.stride as i32,
318             })
319             .collect::<Vec<_>>();
320 
321         self.session.encode(
322             input_buffer_id as i32,
323             // Steal the descriptor of the resource, as libvda will close it.
324             desc.into_raw_descriptor(),
325             &libvda_planes,
326             timestamp as i64,
327             force_keyframe,
328         )?;
329 
330         self.next_input_buffer_id = self.next_input_buffer_id.wrapping_add(1);
331 
332         Ok(input_buffer_id)
333     }
334 
use_output_buffer( &mut self, resource: GuestResourceHandle, offset: u32, size: u32, ) -> VideoResult<OutputBufferId>335     fn use_output_buffer(
336         &mut self,
337         resource: GuestResourceHandle,
338         offset: u32,
339         size: u32,
340     ) -> VideoResult<OutputBufferId> {
341         let output_buffer_id = self.next_output_buffer_id;
342         let desc = match resource {
343             GuestResourceHandle::VirtioObject(handle) => handle.desc,
344             _ => {
345                 return Err(VideoError::BackendFailure(anyhow!(
346                     "VDA backend only supports virtio object resources"
347                 )))
348             }
349         };
350 
351         self.session.use_output_buffer(
352             output_buffer_id as i32,
353             // Steal the descriptor of the resource, as libvda will close it.
354             desc.into_raw_descriptor(),
355             offset,
356             size,
357         )?;
358 
359         self.next_output_buffer_id = self.next_output_buffer_id.wrapping_add(1);
360 
361         Ok(output_buffer_id)
362     }
363 
flush(&mut self) -> VideoResult<()>364     fn flush(&mut self) -> VideoResult<()> {
365         self.session
366             .flush()
367             .context("while flushing")
368             .map_err(VideoError::BackendFailure)
369     }
370 
request_encoding_params_change( &mut self, bitrate: Bitrate, framerate: u32, ) -> VideoResult<()>371     fn request_encoding_params_change(
372         &mut self,
373         bitrate: Bitrate,
374         framerate: u32,
375     ) -> VideoResult<()> {
376         self.session
377             .request_encoding_params_change(bitrate.into(), framerate)
378             .context("while requesting encoder parameter change")
379             .map_err(VideoError::BackendFailure)
380     }
381 
event_pipe(&self) -> &dyn AsRawDescriptor382     fn event_pipe(&self) -> &dyn AsRawDescriptor {
383         self.session.pipe()
384     }
385 
read_event(&mut self) -> VideoResult<EncoderEvent>386     fn read_event(&mut self) -> VideoResult<EncoderEvent> {
387         let event = self.session.read_event()?;
388 
389         use libvda::encode::Event::*;
390         let encoder_event = match event {
391             RequireInputBuffers {
392                 input_count,
393                 input_frame_width,
394                 input_frame_height,
395                 output_buffer_size,
396             } => EncoderEvent::RequireInputBuffers {
397                 input_count,
398                 input_frame_width,
399                 input_frame_height,
400                 output_buffer_size,
401             },
402             ProcessedInputBuffer(id) => EncoderEvent::ProcessedInputBuffer { id: id as u32 },
403             ProcessedOutputBuffer {
404                 output_buffer_id,
405                 payload_size,
406                 key_frame,
407                 timestamp,
408                 ..
409             } => EncoderEvent::ProcessedOutputBuffer {
410                 id: output_buffer_id as u32,
411                 bytesused: payload_size,
412                 keyframe: key_frame,
413                 timestamp: timestamp as u64,
414             },
415             FlushResponse { flush_done } => EncoderEvent::FlushResponse { flush_done },
416             NotifyError(err) => EncoderEvent::NotifyError {
417                 error: VideoError::BackendFailure(anyhow!(err)),
418             },
419         };
420 
421         Ok(encoder_event)
422     }
423 }
424