1 // Copyright 2022 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::BTreeMap; 6 use std::collections::VecDeque; 7 use std::os::raw::c_int; 8 use std::ptr; 9 use std::sync::Arc; 10 use std::sync::Weak; 11 12 use anyhow::anyhow; 13 use anyhow::Context; 14 use base::error; 15 use base::AsRawDescriptor; 16 use base::MappedRegion; 17 use base::MemoryMappingArena; 18 use ffmpeg::avcodec::AvBufferSource; 19 use ffmpeg::avcodec::AvCodec; 20 use ffmpeg::avcodec::AvCodecContext; 21 use ffmpeg::avcodec::AvCodecIterator; 22 use ffmpeg::avcodec::AvFrame; 23 use ffmpeg::avcodec::AvPacket; 24 use ffmpeg::avcodec::Dimensions; 25 use ffmpeg::avcodec::TryReceiveResult; 26 use ffmpeg::max_buffer_alignment; 27 use ffmpeg::AVPictureType_AV_PICTURE_TYPE_I; 28 use ffmpeg::AVRational; 29 use ffmpeg::AV_PKT_FLAG_KEY; 30 31 use crate::virtio::video::encoder::backend::Encoder; 32 use crate::virtio::video::encoder::backend::EncoderSession; 33 use crate::virtio::video::encoder::EncoderCapabilities; 34 use crate::virtio::video::encoder::EncoderEvent; 35 use crate::virtio::video::encoder::InputBufferId; 36 use crate::virtio::video::encoder::OutputBufferId; 37 use crate::virtio::video::encoder::SessionConfig; 38 use crate::virtio::video::error::VideoError; 39 use crate::virtio::video::error::VideoResult; 40 use crate::virtio::video::ffmpeg::TryAsAvFrameExt; 41 use crate::virtio::video::format::Bitrate; 42 use crate::virtio::video::format::Format; 43 use crate::virtio::video::format::FormatDesc; 44 use crate::virtio::video::format::FormatRange; 45 use crate::virtio::video::format::FrameFormat; 46 use crate::virtio::video::format::Profile; 47 use crate::virtio::video::resource::BufferHandle; 48 use crate::virtio::video::resource::GuestResource; 49 use crate::virtio::video::resource::GuestResourceHandle; 50 use crate::virtio::video::utils::EventQueue; 51 use crate::virtio::video::utils::SyncEventQueue; 52 53 /// Structure wrapping a backing memory mapping for an input frame that can be used as a libavcodec 54 /// buffer source. It also sends a `ProcessedInputBuffer` event when dropped. 55 struct InputBuffer { 56 /// Memory mapping to the input frame. 57 mapping: MemoryMappingArena, 58 /// Bistream ID that will be sent as part of the `ProcessedInputBuffer` event. 59 buffer_id: InputBufferId, 60 /// Pointer to the event queue to send the `ProcessedInputBuffer` event to. The event will 61 /// not be sent if the pointer becomes invalid. 62 event_queue: Weak<SyncEventQueue<EncoderEvent>>, 63 } 64 65 impl Drop for InputBuffer { drop(&mut self)66 fn drop(&mut self) { 67 match self.event_queue.upgrade() { 68 None => (), 69 // If the event queue is still valid, send the event signaling we can be reused. 70 Some(event_queue) => event_queue 71 .queue_event(EncoderEvent::ProcessedInputBuffer { id: self.buffer_id }) 72 .unwrap_or_else(|e| { 73 error!("cannot send end of input buffer notification: {:#}", e) 74 }), 75 } 76 } 77 } 78 79 impl AvBufferSource for InputBuffer { as_ptr(&self) -> *const u880 fn as_ptr(&self) -> *const u8 { 81 self.mapping.as_ptr() 82 } 83 len(&self) -> usize84 fn len(&self) -> usize { 85 self.mapping.size() 86 } 87 is_empty(&self) -> bool88 fn is_empty(&self) -> bool { 89 self.len() == 0 90 } 91 } 92 93 enum CodecJob { 94 Frame(AvFrame), 95 Flush, 96 } 97 98 pub struct FfmpegEncoderSession { 99 /// Queue of events waiting to be read by the client. 100 event_queue: Arc<SyncEventQueue<EncoderEvent>>, 101 102 /// FIFO of jobs submitted by the client and waiting to be performed. 103 codec_jobs: VecDeque<CodecJob>, 104 /// Queue of (unfilled) output buffers to fill with upcoming encoder output. 105 output_queue: VecDeque<(OutputBufferId, MemoryMappingArena)>, 106 /// `true` if a flush is pending. While a pending flush exist, input buffers are temporarily 107 /// held on and not sent to the encoder. An actual flush call will be issued when we run out of 108 /// output buffers (to defend against FFmpeg bugs), and we'll try to receive outputs again 109 /// until we receive another code indicating the flush has completed, at which point this 110 /// flag will be reset. 111 is_flushing: bool, 112 113 /// The libav context for this session. 114 context: AvCodecContext, 115 116 next_input_buffer_id: InputBufferId, 117 next_output_buffer_id: OutputBufferId, 118 } 119 120 impl FfmpegEncoderSession { 121 /// Try to send one input frame to the codec for encode. 122 /// 123 /// Returns `Ok(true)` if the frame was successfully queued, `Ok(false)` if the frame was not 124 /// queued due to the queue being full or an in-progress flushing, and `Err` in case of errors. try_send_input_job(&mut self) -> VideoResult<bool>125 fn try_send_input_job(&mut self) -> VideoResult<bool> { 126 // When a flush is queued, drain buffers. 127 if self.is_flushing { 128 return Ok(false); 129 } 130 131 match self.codec_jobs.front() { 132 Some(CodecJob::Frame(b)) => { 133 let result = self 134 .context 135 .try_send_frame(b) 136 .context("while sending frame") 137 .map_err(VideoError::BackendFailure); 138 // This look awkward but we have to do it like this since VideoResult doesn't 139 // implement PartialEq. 140 if let Ok(false) = result { 141 } else { 142 self.codec_jobs.pop_front().unwrap(); 143 } 144 result 145 } 146 Some(CodecJob::Flush) => { 147 self.codec_jobs.pop_front().unwrap(); 148 149 // Queue a flush. The actual flush will be performed when receive returns EAGAIN. 150 self.is_flushing = true; 151 Ok(true) 152 } 153 None => Ok(false), 154 } 155 } 156 157 /// Try to retrieve one encoded packet from the codec, and if success, deliver it to the guest. 158 /// 159 /// Returns `Ok(true)` if the packet was successfully retrieved and the guest was signaled, 160 /// `Ok(false)` if there's no full packet available right now, and `Err` in case of error. try_receive_packet(&mut self) -> VideoResult<bool>161 fn try_receive_packet(&mut self) -> VideoResult<bool> { 162 let (buffer_id, out_buf) = match self.output_queue.front_mut() { 163 Some(p) => p, 164 None => return Ok(false), 165 }; 166 167 let mut packet = AvPacket::empty(); 168 169 match self 170 .context 171 .try_receive_packet(&mut packet) 172 .context("while receiving packet") 173 { 174 Ok(TryReceiveResult::TryAgain) => { 175 if !self.is_flushing { 176 return Ok(false); 177 } 178 179 // Flush the encoder, then move on to draining. 180 if let Err(err) = self.context.flush_encoder() { 181 self.is_flushing = false; 182 self.event_queue 183 .queue_event(EncoderEvent::FlushResponse { flush_done: false }) 184 .context("while flushing") 185 .map_err(VideoError::BackendFailure)?; 186 return Err(err) 187 .context("while flushing") 188 .map_err(VideoError::BackendFailure); 189 } 190 self.try_receive_packet() 191 } 192 Ok(TryReceiveResult::FlushCompleted) => { 193 self.is_flushing = false; 194 self.event_queue 195 .queue_event(EncoderEvent::FlushResponse { flush_done: true }) 196 .map_err(Into::into) 197 .map_err(VideoError::BackendFailure)?; 198 self.context.reset(); 199 Ok(false) 200 } 201 Ok(TryReceiveResult::Received) => { 202 let packet_size = packet.as_ref().size as usize; 203 if packet_size > out_buf.size() { 204 return Err(VideoError::BackendFailure(anyhow!( 205 "encoded packet does not fit in output buffer" 206 ))); 207 } 208 // SAFETY: 209 // Safe because packet.as_ref().data and out_buf.as_ptr() are valid references and 210 // we did bound check above. 211 unsafe { 212 ptr::copy_nonoverlapping(packet.as_ref().data, out_buf.as_ptr(), packet_size); 213 } 214 self.event_queue 215 .queue_event(EncoderEvent::ProcessedOutputBuffer { 216 id: *buffer_id, 217 bytesused: packet.as_ref().size as _, 218 keyframe: (packet.as_ref().flags as u32 & AV_PKT_FLAG_KEY) != 0, 219 timestamp: packet.as_ref().dts as _, 220 }) 221 .map_err(Into::into) 222 .map_err(VideoError::BackendFailure)?; 223 self.output_queue.pop_front(); 224 Ok(true) 225 } 226 Err(e) => Err(VideoError::BackendFailure(e)), 227 } 228 } 229 230 /// Try to progress through the encoding pipeline, either by sending input frames or by 231 /// retrieving output packets and delivering them to the guest. try_encode(&mut self) -> VideoResult<()>232 fn try_encode(&mut self) -> VideoResult<()> { 233 // Go through the pipeline stages as long as it makes some kind of progress. 234 loop { 235 let mut progress = false; 236 // Use |= instead of || to avoid short-circuiting, which is harmless but makes the 237 // execution order weird. 238 progress |= self.try_send_input_job()?; 239 progress |= self.try_receive_packet()?; 240 if !progress { 241 break; 242 } 243 } 244 Ok(()) 245 } 246 } 247 248 impl EncoderSession for FfmpegEncoderSession { encode( &mut self, resource: GuestResource, timestamp: u64, force_keyframe: bool, ) -> VideoResult<InputBufferId>249 fn encode( 250 &mut self, 251 resource: GuestResource, 252 timestamp: u64, 253 force_keyframe: bool, 254 ) -> VideoResult<InputBufferId> { 255 let buffer_id = self.next_input_buffer_id; 256 self.next_input_buffer_id = buffer_id.wrapping_add(1); 257 258 let mut frame: AvFrame = resource 259 .try_as_av_frame(|mapping| InputBuffer { 260 mapping, 261 buffer_id, 262 event_queue: Arc::downgrade(&self.event_queue), 263 }) 264 .context("while creating input AvFrame") 265 .map_err(VideoError::BackendFailure)?; 266 267 if force_keyframe { 268 frame.set_pict_type(AVPictureType_AV_PICTURE_TYPE_I); 269 } 270 frame.set_pts(timestamp as i64); 271 self.codec_jobs.push_back(CodecJob::Frame(frame)); 272 self.try_encode()?; 273 274 Ok(buffer_id) 275 } 276 use_output_buffer( &mut self, resource: GuestResourceHandle, offset: u32, size: u32, ) -> VideoResult<OutputBufferId>277 fn use_output_buffer( 278 &mut self, 279 resource: GuestResourceHandle, 280 offset: u32, 281 size: u32, 282 ) -> VideoResult<OutputBufferId> { 283 let buffer_id = self.next_output_buffer_id; 284 self.next_output_buffer_id = buffer_id.wrapping_add(1); 285 286 let mapping = resource 287 .get_mapping(offset as usize, size as usize) 288 .context("while mapping output buffer") 289 .map_err(VideoError::BackendFailure)?; 290 291 self.output_queue.push_back((buffer_id, mapping)); 292 self.try_encode()?; 293 Ok(buffer_id) 294 } 295 flush(&mut self) -> VideoResult<()>296 fn flush(&mut self) -> VideoResult<()> { 297 if self.is_flushing { 298 return Err(VideoError::BackendFailure(anyhow!( 299 "flush is already in progress" 300 ))); 301 } 302 self.codec_jobs.push_back(CodecJob::Flush); 303 self.try_encode()?; 304 Ok(()) 305 } 306 request_encoding_params_change( &mut self, bitrate: Bitrate, framerate: u32, ) -> VideoResult<()>307 fn request_encoding_params_change( 308 &mut self, 309 bitrate: Bitrate, 310 framerate: u32, 311 ) -> VideoResult<()> { 312 match bitrate { 313 Bitrate::Cbr { target } => { 314 self.context.set_bit_rate(target as u64); 315 } 316 Bitrate::Vbr { target, peak } => { 317 self.context.set_bit_rate(target as u64); 318 self.context.set_max_bit_rate(peak as u64); 319 } 320 } 321 // TODO(b/241492607): support fractional frame rates. 322 self.context.set_time_base(AVRational { 323 num: 1, 324 den: framerate as c_int, 325 }); 326 Ok(()) 327 } 328 event_pipe(&self) -> &dyn AsRawDescriptor329 fn event_pipe(&self) -> &dyn AsRawDescriptor { 330 self.event_queue.as_ref() 331 } 332 read_event(&mut self) -> VideoResult<EncoderEvent>333 fn read_event(&mut self) -> VideoResult<EncoderEvent> { 334 self.event_queue 335 .dequeue_event() 336 .context("while reading encoder event") 337 .map_err(VideoError::BackendFailure) 338 } 339 } 340 341 pub struct FfmpegEncoder { 342 codecs: BTreeMap<Format, AvCodec>, 343 } 344 345 impl FfmpegEncoder { 346 /// Create a new ffmpeg encoder backend instance. new() -> Self347 pub fn new() -> Self { 348 // Find all the encoders supported by libav and store them. 349 let codecs = AvCodecIterator::new() 350 .filter_map(|codec| { 351 if !codec.is_encoder() { 352 return None; 353 } 354 355 let codec_name = codec.name(); 356 357 // Only retain software encoders we know with their corresponding format. Other 358 // encoder might depend on hardware (e.g. *_qsv) which we can't use. 359 let format = match codec_name { 360 "libx264" => Format::H264, 361 "libvpx" => Format::VP8, 362 "libvpx-vp9" => Format::VP9, 363 "libx265" => Format::Hevc, 364 _ => return None, 365 }; 366 367 Some((format, codec)) 368 }) 369 .collect(); 370 371 Self { codecs } 372 } 373 } 374 375 impl Encoder for FfmpegEncoder { 376 type Session = FfmpegEncoderSession; 377 query_capabilities(&self) -> VideoResult<EncoderCapabilities>378 fn query_capabilities(&self) -> VideoResult<EncoderCapabilities> { 379 let codecs = &self.codecs; 380 let mut format_idx = BTreeMap::new(); 381 let mut input_format_descs = vec![]; 382 let output_format_descs = codecs 383 .iter() 384 .enumerate() 385 .map(|(i, (&format, codec))| { 386 let mut in_formats = 0; 387 for in_format in codec.pixel_format_iter() { 388 if let Ok(in_format) = Format::try_from(in_format) { 389 let idx = format_idx.entry(in_format).or_insert_with(|| { 390 let idx = input_format_descs.len(); 391 input_format_descs.push(FormatDesc { 392 mask: 0, 393 format: in_format, 394 frame_formats: vec![FrameFormat { 395 // These frame sizes are arbitrary, but avcodec does not seem to 396 // have any specific restriction in that regard (or any way to 397 // query the supported resolutions). 398 width: FormatRange { 399 min: 64, 400 max: 16384, 401 step: 1, 402 }, 403 height: FormatRange { 404 min: 64, 405 max: 16384, 406 step: 1, 407 }, 408 bitrates: Default::default(), 409 }], 410 plane_align: max_buffer_alignment() as u32, 411 }); 412 idx 413 }); 414 input_format_descs[*idx].mask |= 1 << i; 415 in_formats |= 1 << *idx; 416 } 417 } 418 FormatDesc { 419 mask: in_formats, 420 format, 421 frame_formats: vec![FrameFormat { 422 // These frame sizes are arbitrary, but avcodec does not seem to have any 423 // specific restriction in that regard (or any way to query the supported 424 // resolutions). 425 width: FormatRange { 426 min: 64, 427 max: 16384, 428 step: 1, 429 }, 430 height: FormatRange { 431 min: 64, 432 max: 16384, 433 step: 1, 434 }, 435 bitrates: Default::default(), 436 }], 437 plane_align: max_buffer_alignment() as u32, 438 } 439 }) 440 .collect(); 441 // TODO(ishitatsuyuki): right now we haven't plumbed the profile handling yet and will use 442 // a hard coded set of profiles. Make this support more profiles when 443 // we implement the conversion between virtio and ffmpeg profiles. 444 let coded_format_profiles = codecs 445 .iter() 446 .map(|(&format, _codec)| { 447 ( 448 format, 449 match format { 450 Format::H264 => vec![Profile::H264Baseline], 451 Format::Hevc => vec![Profile::HevcMain], 452 Format::VP8 => vec![Profile::VP8Profile0], 453 Format::VP9 => vec![Profile::VP9Profile0], 454 _ => vec![], 455 }, 456 ) 457 }) 458 .collect(); 459 let caps = EncoderCapabilities { 460 input_format_descs, 461 output_format_descs, 462 coded_format_profiles, 463 }; 464 465 Ok(caps) 466 } 467 start_session(&mut self, config: SessionConfig) -> VideoResult<Self::Session>468 fn start_session(&mut self, config: SessionConfig) -> VideoResult<Self::Session> { 469 let dst_format = config 470 .dst_params 471 .format 472 .ok_or(VideoError::InvalidOperation)?; 473 let codec = self 474 .codecs 475 .get(&dst_format) 476 .ok_or(VideoError::InvalidFormat)?; 477 let pix_fmt = config 478 .src_params 479 .format 480 .ok_or(VideoError::InvalidOperation)? 481 .try_into() 482 .map_err(|_| VideoError::InvalidFormat)?; 483 let context = codec 484 .build_encoder() 485 .and_then(|mut b| { 486 b.set_pix_fmt(pix_fmt); 487 b.set_dimensions(Dimensions { 488 width: config.src_params.frame_width, 489 height: config.src_params.frame_height, 490 }); 491 b.set_time_base(AVRational { 492 num: 1, 493 den: config.frame_rate as _, 494 }); 495 b.build() 496 }) 497 .context("while creating new session") 498 .map_err(VideoError::BackendFailure)?; 499 let session = FfmpegEncoderSession { 500 event_queue: Arc::new( 501 EventQueue::new() 502 .context("while creating encoder session") 503 .map_err(VideoError::BackendFailure)? 504 .into(), 505 ), 506 codec_jobs: Default::default(), 507 output_queue: Default::default(), 508 is_flushing: false, 509 context, 510 next_input_buffer_id: 0, 511 next_output_buffer_id: 0, 512 }; 513 session 514 .event_queue 515 .queue_event(EncoderEvent::RequireInputBuffers { 516 input_count: 4, 517 input_frame_height: config.src_params.frame_height, 518 input_frame_width: config.src_params.frame_width, 519 output_buffer_size: 16 * 1024 * 1024, 520 }) 521 .context("while sending buffer request") 522 .map_err(VideoError::BackendFailure)?; 523 Ok(session) 524 } 525 stop_session(&mut self, _session: Self::Session) -> VideoResult<()>526 fn stop_session(&mut self, _session: Self::Session) -> VideoResult<()> { 527 // Just Drop. 528 Ok(()) 529 } 530 } 531