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