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