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 //! A ffmpeg-based software decoder backend for crosvm. Since it does not require any particular 6 //! harware, it can provide fake hardware acceleration decoding to any guest and is mostly useful in 7 //! order to work on the virtio-video specification, or to implement guest decoder code from the 8 //! comfort of a workstation. 9 //! 10 //! This backend is supposed to serve as the reference implementation for decoding backends in 11 //! crosvm. As such it is fairly complete and exposes all the features and memory types that crosvm 12 //! support. 13 //! 14 //! The code in this main module provides the actual implementation and is free of unsafe code. Safe 15 //! abstractions over the ffmpeg libraries are provided in sub-modules, one per ffmpeg library we 16 //! want to support. 17 18 use std::collections::BTreeMap; 19 use std::collections::VecDeque; 20 use std::sync::Arc; 21 use std::sync::Weak; 22 23 use ::ffmpeg::avcodec::*; 24 use ::ffmpeg::swscale::*; 25 use ::ffmpeg::*; 26 use anyhow::anyhow; 27 use anyhow::Context; 28 use base::error; 29 use base::info; 30 use base::warn; 31 use base::MappedRegion; 32 use base::MemoryMappingArena; 33 use thiserror::Error as ThisError; 34 35 use crate::virtio::video::decoder::backend::*; 36 use crate::virtio::video::ffmpeg::GuestResourceToAvFrameError; 37 use crate::virtio::video::ffmpeg::MemoryMappingAvBufferSource; 38 use crate::virtio::video::ffmpeg::TryAsAvFrameExt; 39 use crate::virtio::video::format::FormatDesc; 40 use crate::virtio::video::format::FormatRange; 41 use crate::virtio::video::format::FrameFormat; 42 use crate::virtio::video::format::Level; 43 use crate::virtio::video::format::Profile; 44 use crate::virtio::video::resource::BufferHandle; 45 use crate::virtio::video::resource::GuestResource; 46 use crate::virtio::video::resource::GuestResourceHandle; 47 use crate::virtio::video::utils::EventQueue; 48 use crate::virtio::video::utils::OutputQueue; 49 use crate::virtio::video::utils::SyncEventQueue; 50 51 /// Structure maintaining a mapping for an encoded input buffer that can be used as a libavcodec 52 /// buffer source. It also sends a `NotifyEndOfBitstreamBuffer` event when dropped. 53 struct InputBuffer { 54 /// Memory mapping to the encoded input data. 55 mapping: MemoryMappingArena, 56 /// Resource ID that we will signal using `NotifyEndOfBitstreamBuffer` upon destruction. 57 resource_id: u32, 58 /// Pointer to the event queue to send the `NotifyEndOfBitstreamBuffer` event to. The event 59 /// will not be sent if the pointer becomes invalid. 60 event_queue: Weak<SyncEventQueue<DecoderEvent>>, 61 } 62 63 impl Drop for InputBuffer { drop(&mut self)64 fn drop(&mut self) { 65 match self.event_queue.upgrade() { 66 None => (), 67 // If the event queue is still valid, send the event signaling we can be reused. 68 Some(event_queue) => event_queue 69 .queue_event(DecoderEvent::NotifyEndOfBitstreamBuffer(self.resource_id)) 70 .unwrap_or_else(|e| { 71 error!("cannot send end of input buffer notification: {:#}", e) 72 }), 73 } 74 } 75 } 76 77 impl AvBufferSource for InputBuffer { as_ptr(&self) -> *const u878 fn as_ptr(&self) -> *const u8 { 79 self.mapping.as_ptr() 80 } 81 len(&self) -> usize82 fn len(&self) -> usize { 83 self.mapping.size() 84 } 85 is_empty(&self) -> bool86 fn is_empty(&self) -> bool { 87 self.len() == 0 88 } 89 } 90 91 /// Types of input job we can receive from the crosvm decoder code. 92 enum CodecJob { 93 Packet(AvPacket<'static>), 94 Flush, 95 } 96 97 /// A crosvm decoder needs to go through a number if setup stages before being able to decode, and 98 /// can require some setup to be redone when a dynamic resolution change occurs. This enum ensures 99 /// that the data associated with a given state only exists when we actually are in this state. 100 enum SessionState { 101 /// Waiting for libavcodec to tell us the resolution of the stream. 102 AwaitingInitialResolution, 103 /// Waiting for the client to call `set_output_buffer_count`. 104 AwaitingBufferCount, 105 /// Decoding and producing frames. 106 Decoding { 107 output_queue: OutputQueue, 108 format_converter: SwConverter, 109 }, 110 /// Dynamic Resolution Change - we can still accept buffers in the old 111 /// format, but are waiting for new parameters before doing any decoding. 112 Drc, 113 } 114 115 /// A decoder session for the ffmpeg backend. 116 pub struct FfmpegDecoderSession { 117 /// Queue of events waiting to be read by the client. 118 event_queue: Arc<SyncEventQueue<DecoderEvent>>, 119 120 /// FIFO of jobs submitted by the client and waiting to be performed. 121 codec_jobs: VecDeque<CodecJob>, 122 /// Whether we are currently flushing. 123 is_flushing: bool, 124 125 /// Current state of the session. 126 state: SessionState, 127 /// Visible size of the decoded frames (width, height). 128 current_visible_res: (usize, usize), 129 130 /// The libav context for this session. 131 context: AvCodecContext, 132 /// The last frame to have been decoded, waiting to be copied into an output buffer and sent 133 /// to the client. 134 avframe: Option<AvFrame>, 135 } 136 137 #[derive(Debug, ThisError)] 138 enum TrySendFrameError { 139 #[error("error while converting frame: {0}")] 140 CannotConvertFrame(#[from] ConversionError), 141 #[error("error while constructing AvFrame: {0}")] 142 IntoAvFrame(#[from] GuestResourceToAvFrameError), 143 #[error("error while sending picture ready event: {0}")] 144 BrokenPipe(#[from] base::Error), 145 } 146 147 #[derive(Debug, ThisError)] 148 enum TryReceiveFrameError { 149 #[error("error creating AvFrame: {0}")] 150 CreateAvFrame(#[from] AvFrameError), 151 #[error("error queueing flush completed event: {0}")] 152 CannotQueueFlushEvent(#[from] base::Error), 153 #[error("error while changing resolution: {0}")] 154 ChangeResolutionError(#[from] ChangeResolutionError), 155 } 156 157 #[derive(Debug, ThisError)] 158 enum TrySendPacketError { 159 #[error("error while sending input packet to libavcodec: {0}")] 160 AvError(#[from] AvError), 161 } 162 163 #[derive(Debug, ThisError)] 164 enum TryDecodeError { 165 #[error("error while sending packet: {0}")] 166 SendPacket(#[from] TrySendPacketError), 167 #[error("error while trying to send decoded frame: {0}")] 168 SendFrameError(#[from] TrySendFrameError), 169 #[error("error while receiving frame: {0}")] 170 ReceiveFrame(#[from] TryReceiveFrameError), 171 } 172 173 #[derive(Debug, ThisError)] 174 enum ChangeResolutionError { 175 #[error("error queueing event: {0}")] 176 QueueEventFailed(#[from] base::Error), 177 #[error("unexpected state during resolution change")] 178 UnexpectedState, 179 } 180 181 impl FfmpegDecoderSession { 182 /// Queue an event for the client to receive. queue_event(&mut self, event: DecoderEvent) -> base::Result<()>183 fn queue_event(&mut self, event: DecoderEvent) -> base::Result<()> { 184 self.event_queue.queue_event(event) 185 } 186 187 /// Start the resolution change process, buffers will now be of size `new_visible_res`. change_resolution( &mut self, new_visible_res: (usize, usize), ) -> Result<(), ChangeResolutionError>188 fn change_resolution( 189 &mut self, 190 new_visible_res: (usize, usize), 191 ) -> Result<(), ChangeResolutionError> { 192 info!("resolution changed to {:?}", new_visible_res); 193 194 // Ask the client for new buffers. 195 self.queue_event(DecoderEvent::ProvidePictureBuffers { 196 min_num_buffers: std::cmp::max(self.context.as_ref().refs, 0) as u32 + 1, 197 width: new_visible_res.0 as i32, 198 height: new_visible_res.1 as i32, 199 visible_rect: Rect { 200 left: 0, 201 top: 0, 202 right: new_visible_res.0 as i32, 203 bottom: new_visible_res.1 as i32, 204 }, 205 })?; 206 207 self.current_visible_res = new_visible_res; 208 209 // Drop our output queue and wait for the new number of output buffers. 210 self.state = match self.state { 211 SessionState::AwaitingInitialResolution => SessionState::AwaitingBufferCount, 212 SessionState::Decoding { .. } => SessionState::Drc, 213 _ => return Err(ChangeResolutionError::UnexpectedState), 214 }; 215 216 Ok(()) 217 } 218 219 /// Try to send one input packet to the codec. 220 /// 221 /// Returns `true` if a packet has successfully been queued, `false` if it could not be, either 222 /// because all pending work has already been queued or because the codec could not accept more 223 /// input at the moment. try_send_packet( &mut self, input_packet: &AvPacket<'static>, ) -> Result<bool, TrySendPacketError>224 fn try_send_packet( 225 &mut self, 226 input_packet: &AvPacket<'static>, 227 ) -> Result<bool, TrySendPacketError> { 228 match self.context.try_send_packet(input_packet) { 229 Ok(true) => Ok(true), 230 // The codec cannot take more input at the moment, we'll try again after we receive some 231 // frames. 232 Ok(false) => Ok(false), 233 // This should happen only if we attempt to submit data while flushing. 234 Err(AvError(AVERROR_EOF)) => Ok(false), 235 // If we got invalid data, keep going in hope that we will catch a valid state later. 236 Err(AvError(AVERROR_INVALIDDATA)) => { 237 warn!("Invalid data in stream, ignoring..."); 238 Ok(true) 239 } 240 Err(e) => Err(e.into()), 241 } 242 } 243 244 /// Try to run the next input job, if any. 245 /// 246 /// Returns `true` if the next job has been submitted, `false` if it could not be, either 247 /// because all pending work has already been queued or because the codec could not accept more 248 /// input at the moment. try_send_input_job(&mut self) -> Result<bool, TrySendPacketError>249 fn try_send_input_job(&mut self) -> Result<bool, TrySendPacketError> { 250 // Do not process any more input while we are flushing. 251 if self.is_flushing { 252 return Ok(false); 253 } 254 255 let mut next_job = match self.codec_jobs.pop_front() { 256 // No work to do at the moment. 257 None => return Ok(false), 258 Some(job) => job, 259 }; 260 261 match &mut next_job { 262 CodecJob::Packet(input_packet) => { 263 let res = self.try_send_packet(input_packet)?; 264 match res { 265 // The input buffer has been processed so we can drop it. 266 true => drop(next_job), 267 // The codec cannot accept new input for now, put the job back into the queue. 268 false => self.codec_jobs.push_front(next_job), 269 } 270 271 Ok(res) 272 } 273 CodecJob::Flush => { 274 // Just set the is_flushing flag for now. We will send the actual flush command when 275 // `try_receive_frame` returns `TryAgain`. This should probably not be necessary but 276 // we sometimes miss the last frame if we send the flush command to libavcodec 277 // earlier (which looks like a bug with libavcodec but needs to be confirmed). 278 self.is_flushing = true; 279 280 Ok(true) 281 } 282 } 283 } 284 285 /// Try to receive a frame from the codec and store it until we emit the corresponding 286 /// `PictureReady` decoder event. 287 /// 288 /// Returns `true` if a frame was successfully retrieved, or false if no frame was available at 289 /// the time, a decoded frame is already waiting to be returned to the client, or the decoder 290 /// needs more input data to proceed further. try_receive_frame(&mut self) -> Result<bool, TryReceiveFrameError>291 fn try_receive_frame(&mut self) -> Result<bool, TryReceiveFrameError> { 292 let mut avframe = match self.avframe { 293 // We already have a frame waiting. Wait until it is sent to process the next one. 294 Some(_) => return Ok(false), 295 None => AvFrame::new()?, 296 }; 297 298 match self.context.try_receive_frame(&mut avframe) { 299 Ok(TryReceiveResult::Received) => { 300 // Now check whether the resolution of the stream has changed. 301 let new_visible_res = (avframe.width as usize, avframe.height as usize); 302 if new_visible_res != self.current_visible_res { 303 self.change_resolution(new_visible_res)?; 304 } 305 306 self.avframe = Some(avframe); 307 308 Ok(true) 309 } 310 Ok(TryReceiveResult::TryAgain) => { 311 if self.is_flushing { 312 // Start flushing. `try_receive_frame` will return `FlushCompleted` when the 313 // flush is completed. `TryAgain` will not be returned again until the flush is 314 // completed. 315 match self.context.flush_decoder() { 316 // Call ourselves again so we can process the flush. 317 Ok(()) => self.try_receive_frame(), 318 Err(err) => { 319 self.is_flushing = false; 320 self.queue_event(DecoderEvent::FlushCompleted(Err( 321 VideoError::BackendFailure(err.into()), 322 )))?; 323 Ok(false) 324 } 325 } 326 } else { 327 // The codec is not ready to output a frame yet. 328 Ok(false) 329 } 330 } 331 Ok(TryReceiveResult::FlushCompleted) => { 332 self.is_flushing = false; 333 self.queue_event(DecoderEvent::FlushCompleted(Ok(())))?; 334 self.context.reset(); 335 Ok(false) 336 } 337 // If we got invalid data, keep going in hope that we will catch a valid state later. 338 Err(AvError(AVERROR_INVALIDDATA)) => { 339 warn!("Invalid data in stream, ignoring..."); 340 Ok(false) 341 } 342 Err(av_err) => { 343 // This is a decoding error, so signal it using a `NotifyError` event to reflect the 344 // same asynchronous flow as a hardware decoder would. 345 if let Err(e) = self.event_queue.queue_event(DecoderEvent::NotifyError( 346 VideoError::BackendFailure(av_err.into()), 347 )) { 348 error!("failed to notify error: {}", e); 349 } 350 Ok(false) 351 } 352 } 353 } 354 355 /// Try to send a pending decoded frame to the client by copying its content into an output 356 /// buffer. 357 /// 358 /// This can only be done if `self.avframe` contains a decoded frame, and an output buffer is 359 /// ready to be written into. 360 /// 361 /// Returns `true` if a frame has been emitted, `false` if the conditions were not met for it to 362 /// happen yet. try_send_frame(&mut self) -> Result<bool, TrySendFrameError>363 fn try_send_frame(&mut self) -> Result<bool, TrySendFrameError> { 364 let (output_queue, format_converter) = match &mut self.state { 365 SessionState::Decoding { 366 output_queue, 367 format_converter, 368 } => (output_queue, format_converter), 369 // Frames can only be emitted if we are actively decoding. 370 _ => return Ok(false), 371 }; 372 373 let avframe = match self.avframe.take() { 374 // No decoded frame available at the moment. 375 None => return Ok(false), 376 Some(avframe) => avframe, 377 }; 378 379 let (picture_buffer_id, target_buffer) = match output_queue.try_get_ready_buffer() { 380 None => { 381 // Keep the decoded frame since we don't have a destination buffer to process it. 382 self.avframe = Some(avframe); 383 return Ok(false); 384 } 385 Some(buffer) => buffer, 386 }; 387 388 // Prepare the picture ready event that we will emit once the frame is written into the 389 // target buffer. 390 let avframe_ref = avframe.as_ref(); 391 let picture_ready_event = DecoderEvent::PictureReady { 392 picture_buffer_id: picture_buffer_id as i32, 393 timestamp: avframe_ref.pts as u64, 394 visible_rect: Rect { 395 left: 0, 396 top: 0, 397 right: avframe_ref.width, 398 bottom: avframe_ref.height, 399 }, 400 }; 401 402 // Convert the frame into the target buffer and emit the picture ready event. 403 format_converter.convert( 404 &avframe, 405 &mut target_buffer.try_as_av_frame(MemoryMappingAvBufferSource::from)?, 406 )?; 407 self.event_queue.queue_event(picture_ready_event)?; 408 409 Ok(true) 410 } 411 412 /// Try to progress as much as possible with decoding. 413 /// 414 /// Our pipeline has three stages: send encoded input to libavcodec, receive decoded frames from 415 /// libavcodec, and copy decoded frames into output buffers sent to the client. This method 416 /// calls these three stages in a loop for as long as at least one makes progress. try_decode(&mut self) -> Result<(), TryDecodeError>417 fn try_decode(&mut self) -> Result<(), TryDecodeError> { 418 // Try to make the pipeline progress as long as one of the stages can move forward 419 while self.try_send_frame()? || self.try_receive_frame()? || self.try_send_input_job()? {} 420 421 Ok(()) 422 } 423 } 424 425 impl DecoderSession for FfmpegDecoderSession { set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()>426 fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()> { 427 match self.state { 428 // It is valid to set an output format before the the initial DRC, but we won't do 429 // anything with it. 430 SessionState::AwaitingInitialResolution => Ok(()), 431 SessionState::AwaitingBufferCount | SessionState::Drc => { 432 let avcontext = self.context.as_ref(); 433 434 let dst_pix_format: AvPixelFormat = 435 format.try_into().map_err(|_| VideoError::InvalidFormat)?; 436 437 self.state = SessionState::Decoding { 438 output_queue: OutputQueue::new(buffer_count), 439 format_converter: SwConverter::new( 440 avcontext.width as usize, 441 avcontext.height as usize, 442 avcontext.pix_fmt, 443 dst_pix_format.pix_fmt(), 444 ) 445 .context("while setting output parameters") 446 .map_err(VideoError::BackendFailure)?, 447 }; 448 Ok(()) 449 } 450 _ => Err(VideoError::BackendFailure(anyhow!( 451 "invalid state while calling set_output_parameters" 452 ))), 453 } 454 } 455 decode( &mut self, resource_id: u32, timestamp: u64, resource: GuestResourceHandle, offset: u32, bytes_used: u32, ) -> VideoResult<()>456 fn decode( 457 &mut self, 458 resource_id: u32, 459 timestamp: u64, 460 resource: GuestResourceHandle, 461 offset: u32, 462 bytes_used: u32, 463 ) -> VideoResult<()> { 464 let input_buffer = InputBuffer { 465 mapping: resource 466 .get_mapping(offset as usize, bytes_used as usize) 467 .context("while mapping input buffer") 468 .map_err(VideoError::BackendFailure)?, 469 resource_id, 470 event_queue: Arc::downgrade(&self.event_queue), 471 }; 472 473 let avbuffer = AvBuffer::new(input_buffer) 474 .context("while creating AvPacket") 475 .map_err(VideoError::BackendFailure)?; 476 477 let avpacket = AvPacket::new_owned(timestamp as i64, avbuffer); 478 479 self.codec_jobs.push_back(CodecJob::Packet(avpacket)); 480 481 self.try_decode() 482 .context("while decoding") 483 .map_err(VideoError::BackendFailure) 484 } 485 flush(&mut self) -> VideoResult<()>486 fn flush(&mut self) -> VideoResult<()> { 487 if self.is_flushing { 488 Err(VideoError::BackendFailure(anyhow!( 489 "flush is already in progress" 490 ))) 491 } else { 492 self.codec_jobs.push_back(CodecJob::Flush); 493 self.try_decode() 494 .context("while flushing") 495 .map_err(VideoError::BackendFailure) 496 } 497 } 498 reset(&mut self) -> VideoResult<()>499 fn reset(&mut self) -> VideoResult<()> { 500 // Reset the codec. 501 self.context.reset(); 502 503 // Drop all currently pending jobs. 504 self.codec_jobs.clear(); 505 506 // Drop the queued output buffers. 507 self.clear_output_buffers()?; 508 509 self.queue_event(DecoderEvent::ResetCompleted(Ok(()))) 510 .context("while resetting") 511 .map_err(VideoError::BackendFailure) 512 } 513 clear_output_buffers(&mut self) -> VideoResult<()>514 fn clear_output_buffers(&mut self) -> VideoResult<()> { 515 // Cancel any ongoing flush. 516 self.is_flushing = false; 517 518 // Drop all output buffers we currently hold. 519 if let SessionState::Decoding { output_queue, .. } = &mut self.state { 520 output_queue.clear_ready_buffers(); 521 } 522 523 // Drop the currently decoded frame. 524 self.avframe = None; 525 526 // Drop all decoded frames signaled as ready and cancel any reported flush. 527 self.event_queue.retain(|event| { 528 !matches!( 529 event, 530 DecoderEvent::PictureReady { .. } | DecoderEvent::FlushCompleted(_) 531 ) 532 }); 533 534 Ok(()) 535 } 536 event_pipe(&self) -> &dyn AsRawDescriptor537 fn event_pipe(&self) -> &dyn AsRawDescriptor { 538 self.event_queue.as_ref() 539 } 540 use_output_buffer( &mut self, picture_buffer_id: i32, resource: GuestResource, ) -> VideoResult<()>541 fn use_output_buffer( 542 &mut self, 543 picture_buffer_id: i32, 544 resource: GuestResource, 545 ) -> VideoResult<()> { 546 let output_queue = match &mut self.state { 547 // It is valid to receive buffers before the the initial DRC, but we won't decode 548 // anything into them. 549 SessionState::AwaitingInitialResolution => return Ok(()), 550 SessionState::Decoding { output_queue, .. } => output_queue, 551 // Receiving buffers during DRC is valid, but we won't use them and can just drop them. 552 SessionState::Drc => return Ok(()), 553 _ => { 554 error!("use_output_buffer: invalid state"); 555 return Ok(()); 556 } 557 }; 558 559 output_queue 560 .import_buffer(picture_buffer_id as u32, resource) 561 .context("while importing output buffer") 562 .map_err(VideoError::BackendFailure)?; 563 self.try_decode() 564 .context("while importing output buffer") 565 .map_err(VideoError::BackendFailure) 566 } 567 reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()>568 fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()> { 569 let output_queue = match &mut self.state { 570 // It is valid to receive buffers before the the initial DRC, but we won't decode 571 // anything into them. 572 SessionState::AwaitingInitialResolution => return Ok(()), 573 SessionState::Decoding { output_queue, .. } => output_queue, 574 // Reusing buffers during DRC is valid, but we won't use them and can just drop them. 575 SessionState::Drc => return Ok(()), 576 _ => { 577 return Err(VideoError::BackendFailure(anyhow!( 578 "invalid state while calling reuse_output_buffer" 579 ))) 580 } 581 }; 582 583 output_queue 584 .reuse_buffer(picture_buffer_id as u32) 585 .context("while reusing output buffer") 586 .map_err(VideoError::BackendFailure)?; 587 self.try_decode() 588 .context("while reusing output buffer") 589 .map_err(VideoError::BackendFailure) 590 } 591 read_event(&mut self) -> VideoResult<DecoderEvent>592 fn read_event(&mut self) -> VideoResult<DecoderEvent> { 593 self.event_queue 594 .dequeue_event() 595 .context("while reading decoder event") 596 .map_err(VideoError::BackendFailure) 597 } 598 } 599 600 pub struct FfmpegDecoder { 601 codecs: BTreeMap<Format, AvCodec>, 602 } 603 604 impl FfmpegDecoder { 605 /// Create a new ffmpeg decoder backend instance. new() -> Self606 pub fn new() -> Self { 607 // Find all the decoders supported by libav and store them. 608 let codecs = AvCodecIterator::new() 609 .filter_map(|codec| { 610 if !codec.is_decoder() { 611 return None; 612 } 613 614 let codec_name = codec.name(); 615 616 // Only keep processing the decoders we are interested in. These are all software 617 // decoders, but nothing prevents us from supporting hardware-accelerated ones 618 // (e.g. *_qsv for VAAPI-based acceleration) in the future! 619 let format = match codec_name { 620 "h264" => Format::H264, 621 "vp8" => Format::VP8, 622 "vp9" => Format::VP9, 623 "hevc" => Format::Hevc, 624 _ => return None, 625 }; 626 627 // We require custom buffer allocators, so ignore codecs that are not capable of 628 // using them. 629 if codec.capabilities() & AV_CODEC_CAP_DR1 == 0 { 630 warn!( 631 "Skipping codec {} due to lack of DR1 capability.", 632 codec_name 633 ); 634 return None; 635 } 636 637 Some((format, codec)) 638 }) 639 .collect(); 640 641 Self { codecs } 642 } 643 } 644 645 impl DecoderBackend for FfmpegDecoder { 646 type Session = FfmpegDecoderSession; 647 get_capabilities(&self) -> Capability648 fn get_capabilities(&self) -> Capability { 649 // The virtio device only supports NV12 for now it seems... 650 const SUPPORTED_OUTPUT_FORMATS: [Format; 1] = [Format::NV12]; 651 652 let mut in_formats = vec![]; 653 let mut profiles_map: BTreeMap<Format, Vec<Profile>> = Default::default(); 654 let mut levels: BTreeMap<Format, Vec<Level>> = Default::default(); 655 for (&format, codec) in &self.codecs { 656 let profile_iter = codec.profile_iter(); 657 let profiles = match format { 658 Format::H264 => { 659 // We only support Level 1.0 for H.264. 660 // TODO Do we? Why? 661 levels.insert(format, vec![Level::H264_1_0]); 662 663 profile_iter 664 .filter_map(|p| { 665 match p.profile() { 666 FF_PROFILE_H264_BASELINE => Some(Profile::H264Baseline), 667 FF_PROFILE_H264_MAIN => Some(Profile::H264Main), 668 FF_PROFILE_H264_EXTENDED => Some(Profile::H264Extended), 669 FF_PROFILE_H264_HIGH => Some(Profile::H264High), 670 FF_PROFILE_H264_HIGH_10 => Some(Profile::H264High10), 671 FF_PROFILE_H264_HIGH_422 => Some(Profile::H264High422), 672 FF_PROFILE_H264_HIGH_444_PREDICTIVE => { 673 Some(Profile::H264High444PredictiveProfile) 674 } 675 FF_PROFILE_H264_STEREO_HIGH => Some(Profile::H264StereoHigh), 676 FF_PROFILE_H264_MULTIVIEW_HIGH => Some(Profile::H264MultiviewHigh), 677 // TODO H264ScalableBaseline and H264ScalableHigh have no libav 678 // equivalents? 679 _ => None, 680 } 681 }) 682 .collect() 683 } 684 Format::VP8 => { 685 // FFmpeg has no VP8 profiles, for some reason... 686 vec![ 687 Profile::VP8Profile0, 688 Profile::VP8Profile1, 689 Profile::VP8Profile2, 690 Profile::VP8Profile3, 691 ] 692 } 693 Format::VP9 => profile_iter 694 .filter_map(|p| match p.profile() { 695 FF_PROFILE_VP9_0 => Some(Profile::VP9Profile0), 696 FF_PROFILE_VP9_1 => Some(Profile::VP9Profile1), 697 FF_PROFILE_VP9_2 => Some(Profile::VP9Profile2), 698 FF_PROFILE_VP9_3 => Some(Profile::VP9Profile3), 699 _ => None, 700 }) 701 .collect(), 702 Format::Hevc => profile_iter 703 .filter_map(|p| match p.profile() { 704 FF_PROFILE_HEVC_MAIN => Some(Profile::HevcMain), 705 FF_PROFILE_HEVC_MAIN_10 => Some(Profile::HevcMain10), 706 FF_PROFILE_HEVC_MAIN_STILL_PICTURE => Some(Profile::HevcMainStillPicture), 707 _ => None, 708 }) 709 .collect(), 710 _ => unreachable!("Unhandled format {:?}", format), 711 }; 712 713 profiles_map.insert(format, profiles); 714 715 in_formats.push(FormatDesc { 716 mask: !(u64::MAX << SUPPORTED_OUTPUT_FORMATS.len()), 717 format, 718 frame_formats: vec![FrameFormat { 719 // These frame sizes are arbitrary, but avcodec does not seem to have any 720 // specific restriction in that regard (or any way to query the supported 721 // resolutions). 722 width: FormatRange { 723 min: 64, 724 max: 16384, 725 step: 1, 726 }, 727 height: FormatRange { 728 min: 64, 729 max: 16384, 730 step: 1, 731 }, 732 bitrates: Default::default(), 733 }], 734 plane_align: max_buffer_alignment() as u32, 735 }); 736 } 737 738 // We support all output formats through the use of swscale(). 739 let out_formats = SUPPORTED_OUTPUT_FORMATS 740 .iter() 741 .map(|&format| FormatDesc { 742 mask: !(u64::MAX << in_formats.len()), 743 format, 744 frame_formats: vec![FrameFormat { 745 // These frame sizes are arbitrary, but avcodec does not seem to have any 746 // specific restriction in that regard (or any way to query the supported 747 // resolutions). 748 width: FormatRange { 749 min: 64, 750 max: 16384, 751 step: 1, 752 }, 753 height: FormatRange { 754 min: 64, 755 max: 16384, 756 step: 1, 757 }, 758 bitrates: Default::default(), 759 }], 760 plane_align: max_buffer_alignment() as u32, 761 }) 762 .collect::<Vec<_>>(); 763 764 Capability::new(in_formats, out_formats, profiles_map, levels) 765 } 766 new_session(&mut self, format: Format) -> VideoResult<Self::Session>767 fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> { 768 let codec = self.codecs.get(&format).ok_or(VideoError::InvalidFormat)?; 769 let context = codec 770 // TODO we should use a custom `get_buffer` function that renders directly into the 771 // target buffer if the output format is directly supported by libavcodec. Right now 772 // libavcodec is allocating its own frame buffers, which forces us to perform a copy. 773 .build_decoder() 774 .and_then(|b| b.build()) 775 .context("while creating new session") 776 .map_err(VideoError::BackendFailure)?; 777 Ok(FfmpegDecoderSession { 778 codec_jobs: Default::default(), 779 is_flushing: false, 780 state: SessionState::AwaitingInitialResolution, 781 event_queue: Arc::new( 782 EventQueue::new() 783 .context("while creating decoder session") 784 .map_err(VideoError::BackendFailure)? 785 .into(), 786 ), 787 context, 788 current_visible_res: (0, 0), 789 avframe: None, 790 }) 791 } 792 } 793 794 #[cfg(test)] 795 mod tests { 796 use super::super::tests::*; 797 use super::*; 798 799 #[test] test_get_capabilities()800 fn test_get_capabilities() { 801 let decoder = FfmpegDecoder::new(); 802 let caps = decoder.get_capabilities(); 803 assert!(!caps.input_formats().is_empty()); 804 assert!(!caps.output_formats().is_empty()); 805 } 806 807 #[test] test_decode_h264_guestmem_to_guestmem()808 fn test_decode_h264_guestmem_to_guestmem() { 809 decode_h264_generic( 810 &mut FfmpegDecoder::new(), 811 build_guest_mem_handle, 812 build_guest_mem_handle, 813 ); 814 } 815 816 // Decode using guest memory input and virtio object output buffers. 817 #[test] test_decode_h264_guestmem_to_object()818 fn test_decode_h264_guestmem_to_object() { 819 decode_h264_generic( 820 &mut FfmpegDecoder::new(), 821 build_guest_mem_handle, 822 build_object_handle, 823 ); 824 } 825 826 // Decode using virtio object input and guest memory output buffers. 827 #[test] test_decode_h264_object_to_guestmem()828 fn test_decode_h264_object_to_guestmem() { 829 decode_h264_generic( 830 &mut FfmpegDecoder::new(), 831 build_object_handle, 832 build_guest_mem_handle, 833 ); 834 } 835 836 // Decode using virtio object input and output buffers. 837 #[test] test_decode_h264_object_to_object()838 fn test_decode_h264_object_to_object() { 839 decode_h264_generic( 840 &mut FfmpegDecoder::new(), 841 build_object_handle, 842 build_object_handle, 843 ); 844 } 845 } 846