• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::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.len() == 0 || output_formats.len() == 0 {
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 `max_framerate_denominator`.
125             let libvda::encode::OutputProfile {
126                 profile: libvda_profile,
127                 max_width,
128                 max_height,
129                 ..
130             } = output_format;
131 
132             let profile = match Profile::from_libvda_profile(*libvda_profile) {
133                 Some(p) => p,
134                 None => {
135                     warn!("Skipping unsupported libvda profile: {:?}", libvda_profile);
136                     continue;
137                 }
138             };
139 
140             match parsed_formats.entry(profile.to_format()) {
141                 Entry::Occupied(mut occupied_entry) => {
142                     let parsed_format = occupied_entry.get_mut();
143                     parsed_format.profiles.push(profile);
144                     // If we get different libvda profiles of the same VIRTIO_VIDEO_FORMAT
145                     // (Format) that have different max resolutions or bitrates, take the
146                     // minimum between all of the different profiles.
147                     parsed_format.max_width = std::cmp::min(*max_width, parsed_format.max_width);
148                     parsed_format.max_height = std::cmp::min(*max_height, parsed_format.max_height);
149                 }
150                 Entry::Vacant(vacant_entry) => {
151                     vacant_entry.insert(ParsedFormat {
152                         profiles: vec![profile],
153                         max_width: *max_width,
154                         max_height: *max_height,
155                     });
156                 }
157             }
158         }
159 
160         let mut output_format_descs = vec![];
161         let mut coded_format_profiles = BTreeMap::new();
162         for (format, parsed_format) in parsed_formats.into_iter() {
163             let ParsedFormat {
164                 mut profiles,
165                 max_width,
166                 max_height,
167             } = parsed_format;
168 
169             output_format_descs.push(FormatDesc {
170                 mask: !(u64::MAX << output_formats.len()),
171                 format,
172                 frame_formats: vec![FrameFormat {
173                     width: FormatRange {
174                         min: 2,
175                         max: max_width,
176                         step: 1,
177                     },
178                     height: FormatRange {
179                         min: 2,
180                         max: max_height,
181                         step: 1,
182                     },
183                     bitrates: vec![FormatRange {
184                         min: 0,
185                         max: 8000,
186                         step: 1,
187                     }],
188                 }],
189                 plane_align: 1,
190             });
191 
192             profiles.sort_unstable();
193             coded_format_profiles.insert(format, profiles);
194         }
195 
196         Ok(LibvdaEncoder {
197             instance,
198             capabilities: EncoderCapabilities {
199                 input_format_descs,
200                 output_format_descs,
201                 coded_format_profiles,
202             },
203         })
204     }
205 }
206 
207 impl Encoder for LibvdaEncoder {
208     type Session = LibvdaEncoderSession;
209 
query_capabilities(&self) -> VideoResult<EncoderCapabilities>210     fn query_capabilities(&self) -> VideoResult<EncoderCapabilities> {
211         Ok(self.capabilities.clone())
212     }
213 
start_session(&mut self, config: SessionConfig) -> VideoResult<LibvdaEncoderSession>214     fn start_session(&mut self, config: SessionConfig) -> VideoResult<LibvdaEncoderSession> {
215         if config.dst_params.format.is_none() {
216             return Err(VideoError::InvalidArgument);
217         }
218 
219         let input_format = match config
220             .src_params
221             .format
222             .ok_or(VideoError::InvalidArgument)?
223         {
224             Format::NV12 => libvda::PixelFormat::NV12,
225             Format::YUV420 => libvda::PixelFormat::YV12,
226             unsupported_format => {
227                 error!("Unsupported libvda format: {}", unsupported_format);
228                 return Err(VideoError::InvalidArgument);
229             }
230         };
231 
232         let output_profile = match config.dst_profile.to_libvda_profile() {
233             Some(p) => p,
234             None => {
235                 error!("Unsupported libvda profile");
236                 return Err(VideoError::InvalidArgument);
237             }
238         };
239 
240         let config = libvda::encode::Config {
241             input_format,
242             input_visible_width: config.src_params.frame_width,
243             input_visible_height: config.src_params.frame_height,
244             output_profile,
245             bitrate: config.dst_bitrate.into(),
246             initial_framerate: if config.frame_rate == 0 {
247                 None
248             } else {
249                 Some(config.frame_rate)
250             },
251             h264_output_level: config.dst_h264_level.map(|level| {
252                 // This value is aligned to the H264 standard definition of SPS.level_idc.
253                 match level {
254                     Level::H264_1_0 => 10,
255                     Level::H264_1_1 => 11,
256                     Level::H264_1_2 => 12,
257                     Level::H264_1_3 => 13,
258                     Level::H264_2_0 => 20,
259                     Level::H264_2_1 => 21,
260                     Level::H264_2_2 => 22,
261                     Level::H264_3_0 => 30,
262                     Level::H264_3_1 => 31,
263                     Level::H264_3_2 => 32,
264                     Level::H264_4_0 => 40,
265                     Level::H264_4_1 => 41,
266                     Level::H264_4_2 => 42,
267                     Level::H264_5_0 => 50,
268                     Level::H264_5_1 => 51,
269                 }
270             }),
271         };
272 
273         let session = self.instance.open_session(config)?;
274 
275         Ok(LibvdaEncoderSession {
276             session,
277             next_input_buffer_id: 1,
278             next_output_buffer_id: 1,
279         })
280     }
281 
stop_session(&mut self, _session: LibvdaEncoderSession) -> VideoResult<()>282     fn stop_session(&mut self, _session: LibvdaEncoderSession) -> VideoResult<()> {
283         // Resources will be freed when `_session` is dropped.
284         Ok(())
285     }
286 }
287 
288 pub struct LibvdaEncoderSession {
289     session: libvda::encode::Session,
290     next_input_buffer_id: InputBufferId,
291     next_output_buffer_id: OutputBufferId,
292 }
293 
294 impl EncoderSession for LibvdaEncoderSession {
encode( &mut self, resource: GuestResource, timestamp: u64, force_keyframe: bool, ) -> VideoResult<InputBufferId>295     fn encode(
296         &mut self,
297         resource: GuestResource,
298         timestamp: u64,
299         force_keyframe: bool,
300     ) -> VideoResult<InputBufferId> {
301         let input_buffer_id = self.next_input_buffer_id;
302         let desc = match resource.handle {
303             GuestResourceHandle::VirtioObject(handle) => handle.desc,
304             _ => {
305                 return Err(VideoError::BackendFailure(anyhow!(
306                     "VDA backend only supports virtio object resources"
307                 )))
308             }
309         };
310 
311         let libvda_planes = resource
312             .planes
313             .iter()
314             .map(|plane| libvda::FramePlane {
315                 offset: plane.offset as i32,
316                 stride: plane.stride as i32,
317             })
318             .collect::<Vec<_>>();
319 
320         self.session.encode(
321             input_buffer_id as i32,
322             // Steal the descriptor of the resource, as libvda will close it.
323             desc.into_raw_descriptor(),
324             &libvda_planes,
325             timestamp as i64,
326             force_keyframe,
327         )?;
328 
329         self.next_input_buffer_id = self.next_input_buffer_id.wrapping_add(1);
330 
331         Ok(input_buffer_id)
332     }
333 
use_output_buffer( &mut self, resource: GuestResourceHandle, offset: u32, size: u32, ) -> VideoResult<OutputBufferId>334     fn use_output_buffer(
335         &mut self,
336         resource: GuestResourceHandle,
337         offset: u32,
338         size: u32,
339     ) -> VideoResult<OutputBufferId> {
340         let output_buffer_id = self.next_output_buffer_id;
341         let desc = match resource {
342             GuestResourceHandle::VirtioObject(handle) => handle.desc,
343             _ => {
344                 return Err(VideoError::BackendFailure(anyhow!(
345                     "VDA backend only supports virtio object resources"
346                 )))
347             }
348         };
349 
350         self.session.use_output_buffer(
351             output_buffer_id as i32,
352             // Steal the descriptor of the resource, as libvda will close it.
353             desc.into_raw_descriptor(),
354             offset,
355             size,
356         )?;
357 
358         self.next_output_buffer_id = self.next_output_buffer_id.wrapping_add(1);
359 
360         Ok(output_buffer_id)
361     }
362 
flush(&mut self) -> VideoResult<()>363     fn flush(&mut self) -> VideoResult<()> {
364         self.session
365             .flush()
366             .context("while flushing")
367             .map_err(VideoError::BackendFailure)
368     }
369 
request_encoding_params_change( &mut self, bitrate: Bitrate, framerate: u32, ) -> VideoResult<()>370     fn request_encoding_params_change(
371         &mut self,
372         bitrate: Bitrate,
373         framerate: u32,
374     ) -> VideoResult<()> {
375         self.session
376             .request_encoding_params_change(bitrate.into(), framerate)
377             .context("while requesting encoder parameter change")
378             .map_err(VideoError::BackendFailure)
379     }
380 
event_pipe(&self) -> &dyn AsRawDescriptor381     fn event_pipe(&self) -> &dyn AsRawDescriptor {
382         self.session.pipe()
383     }
384 
read_event(&mut self) -> VideoResult<EncoderEvent>385     fn read_event(&mut self) -> VideoResult<EncoderEvent> {
386         let event = self.session.read_event()?;
387 
388         use libvda::encode::Event::*;
389         let encoder_event = match event {
390             RequireInputBuffers {
391                 input_count,
392                 input_frame_width,
393                 input_frame_height,
394                 output_buffer_size,
395             } => EncoderEvent::RequireInputBuffers {
396                 input_count,
397                 input_frame_width,
398                 input_frame_height,
399                 output_buffer_size,
400             },
401             ProcessedInputBuffer(id) => EncoderEvent::ProcessedInputBuffer { id: id as u32 },
402             ProcessedOutputBuffer {
403                 output_buffer_id,
404                 payload_size,
405                 key_frame,
406                 timestamp,
407                 ..
408             } => EncoderEvent::ProcessedOutputBuffer {
409                 id: output_buffer_id as u32,
410                 bytesused: payload_size,
411                 keyframe: key_frame,
412                 timestamp: timestamp as u64,
413             },
414             FlushResponse { flush_done } => EncoderEvent::FlushResponse { flush_done },
415             NotifyError(err) => EncoderEvent::NotifyError {
416                 error: VideoError::BackendFailure(anyhow!(err)),
417             },
418         };
419 
420         Ok(encoder_event)
421     }
422 }
423