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 picture_ready_event = DecoderEvent::PictureReady { 391 picture_buffer_id: picture_buffer_id as i32, 392 timestamp: avframe.pts as u64, 393 }; 394 395 // Convert the frame into the target buffer and emit the picture ready event. 396 format_converter.convert( 397 &avframe, 398 &mut target_buffer.try_as_av_frame(MemoryMappingAvBufferSource::from)?, 399 )?; 400 self.event_queue.queue_event(picture_ready_event)?; 401 402 Ok(true) 403 } 404 405 /// Try to progress as much as possible with decoding. 406 /// 407 /// Our pipeline has three stages: send encoded input to libavcodec, receive decoded frames from 408 /// libavcodec, and copy decoded frames into output buffers sent to the client. This method 409 /// calls these three stages in a loop for as long as at least one makes progress. try_decode(&mut self) -> Result<(), TryDecodeError>410 fn try_decode(&mut self) -> Result<(), TryDecodeError> { 411 // Try to make the pipeline progress as long as one of the stages can move forward 412 while self.try_send_frame()? || self.try_receive_frame()? || self.try_send_input_job()? {} 413 414 Ok(()) 415 } 416 } 417 418 impl DecoderSession for FfmpegDecoderSession { set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()>419 fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()> { 420 match self.state { 421 // It is valid to set an output format before the the initial DRC, but we won't do 422 // anything with it. 423 SessionState::AwaitingInitialResolution => Ok(()), 424 SessionState::AwaitingBufferCount | SessionState::Drc => { 425 let avcontext = self.context.as_ref(); 426 427 let dst_pix_format: AvPixelFormat = 428 format.try_into().map_err(|_| VideoError::InvalidFormat)?; 429 430 self.state = SessionState::Decoding { 431 output_queue: OutputQueue::new(buffer_count), 432 format_converter: SwConverter::new( 433 avcontext.width as usize, 434 avcontext.height as usize, 435 avcontext.pix_fmt, 436 dst_pix_format.pix_fmt(), 437 ) 438 .context("while setting output parameters") 439 .map_err(VideoError::BackendFailure)?, 440 }; 441 Ok(()) 442 } 443 _ => Err(VideoError::BackendFailure(anyhow!( 444 "invalid state while calling set_output_parameters" 445 ))), 446 } 447 } 448 decode( &mut self, resource_id: u32, timestamp: u64, resource: GuestResourceHandle, offset: u32, bytes_used: u32, ) -> VideoResult<()>449 fn decode( 450 &mut self, 451 resource_id: u32, 452 timestamp: u64, 453 resource: GuestResourceHandle, 454 offset: u32, 455 bytes_used: u32, 456 ) -> VideoResult<()> { 457 let input_buffer = InputBuffer { 458 mapping: resource 459 .get_mapping(offset as usize, bytes_used as usize) 460 .context("while mapping input buffer") 461 .map_err(VideoError::BackendFailure)?, 462 resource_id, 463 event_queue: Arc::downgrade(&self.event_queue), 464 }; 465 466 let avbuffer = AvBuffer::new(input_buffer) 467 .context("while creating AvPacket") 468 .map_err(VideoError::BackendFailure)?; 469 470 let avpacket = AvPacket::new_owned(timestamp as i64, avbuffer); 471 472 self.codec_jobs.push_back(CodecJob::Packet(avpacket)); 473 474 self.try_decode() 475 .context("while decoding") 476 .map_err(VideoError::BackendFailure) 477 } 478 flush(&mut self) -> VideoResult<()>479 fn flush(&mut self) -> VideoResult<()> { 480 if self.is_flushing { 481 Err(VideoError::BackendFailure(anyhow!( 482 "flush is already in progress" 483 ))) 484 } else { 485 self.codec_jobs.push_back(CodecJob::Flush); 486 self.try_decode() 487 .context("while flushing") 488 .map_err(VideoError::BackendFailure) 489 } 490 } 491 reset(&mut self) -> VideoResult<()>492 fn reset(&mut self) -> VideoResult<()> { 493 // Reset the codec. 494 self.context.reset(); 495 496 // Drop all currently pending jobs. 497 self.codec_jobs.clear(); 498 499 // Drop the queued output buffers. 500 self.clear_output_buffers()?; 501 502 self.queue_event(DecoderEvent::ResetCompleted(Ok(()))) 503 .context("while resetting") 504 .map_err(VideoError::BackendFailure) 505 } 506 clear_output_buffers(&mut self) -> VideoResult<()>507 fn clear_output_buffers(&mut self) -> VideoResult<()> { 508 // Cancel any ongoing flush. 509 self.is_flushing = false; 510 511 // Drop all output buffers we currently hold. 512 if let SessionState::Decoding { output_queue, .. } = &mut self.state { 513 output_queue.clear_ready_buffers(); 514 } 515 516 // Drop the currently decoded frame. 517 self.avframe = None; 518 519 // Drop all decoded frames signaled as ready and cancel any reported flush. 520 self.event_queue.retain(|event| { 521 !matches!( 522 event, 523 DecoderEvent::PictureReady { .. } | DecoderEvent::FlushCompleted(_) 524 ) 525 }); 526 527 Ok(()) 528 } 529 event_pipe(&self) -> &dyn AsRawDescriptor530 fn event_pipe(&self) -> &dyn AsRawDescriptor { 531 self.event_queue.as_ref() 532 } 533 use_output_buffer( &mut self, picture_buffer_id: i32, resource: GuestResource, ) -> VideoResult<()>534 fn use_output_buffer( 535 &mut self, 536 picture_buffer_id: i32, 537 resource: GuestResource, 538 ) -> VideoResult<()> { 539 let output_queue = match &mut self.state { 540 // It is valid to receive buffers before the the initial DRC, but we won't decode 541 // anything into them. 542 SessionState::AwaitingInitialResolution => return Ok(()), 543 SessionState::Decoding { output_queue, .. } => output_queue, 544 // Receiving buffers during DRC is valid, but we won't use them and can just drop them. 545 SessionState::Drc => return Ok(()), 546 _ => { 547 error!("use_output_buffer: invalid state"); 548 return Ok(()); 549 } 550 }; 551 552 output_queue 553 .import_buffer(picture_buffer_id as u32, resource) 554 .context("while importing output buffer") 555 .map_err(VideoError::BackendFailure)?; 556 self.try_decode() 557 .context("while decoding output buffer") 558 .map_err(VideoError::BackendFailure) 559 } 560 reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()>561 fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()> { 562 let output_queue = match &mut self.state { 563 // It is valid to receive buffers before the the initial DRC, but we won't decode 564 // anything into them. 565 SessionState::AwaitingInitialResolution => return Ok(()), 566 SessionState::Decoding { output_queue, .. } => output_queue, 567 // Reusing buffers during DRC is valid, but we won't use them and can just drop them. 568 SessionState::Drc => return Ok(()), 569 _ => { 570 return Err(VideoError::BackendFailure(anyhow!( 571 "invalid state while calling reuse_output_buffer" 572 ))) 573 } 574 }; 575 576 output_queue 577 .reuse_buffer(picture_buffer_id as u32) 578 .context("while reusing output buffer") 579 .map_err(VideoError::BackendFailure)?; 580 self.try_decode() 581 .context("while reusing output buffer") 582 .map_err(VideoError::BackendFailure) 583 } 584 read_event(&mut self) -> VideoResult<DecoderEvent>585 fn read_event(&mut self) -> VideoResult<DecoderEvent> { 586 self.event_queue 587 .dequeue_event() 588 .context("while reading decoder event") 589 .map_err(VideoError::BackendFailure) 590 } 591 } 592 593 pub struct FfmpegDecoder { 594 codecs: BTreeMap<Format, AvCodec>, 595 } 596 597 impl FfmpegDecoder { 598 /// Create a new ffmpeg decoder backend instance. new() -> Self599 pub fn new() -> Self { 600 // Find all the decoders supported by libav and store them. 601 let codecs = AvCodecIterator::new() 602 .filter_map(|codec| { 603 if !codec.is_decoder() { 604 return None; 605 } 606 607 let codec_name = codec.name(); 608 609 // Only keep processing the decoders we are interested in. These are all software 610 // decoders, but nothing prevents us from supporting hardware-accelerated ones 611 // (e.g. *_qsv for VAAPI-based acceleration) in the future! 612 let format = match codec_name { 613 "h264" => Format::H264, 614 "vp8" => Format::VP8, 615 "vp9" => Format::VP9, 616 "hevc" => Format::Hevc, 617 _ => return None, 618 }; 619 620 // We require custom buffer allocators, so ignore codecs that are not capable of 621 // using them. 622 if codec.capabilities() & AV_CODEC_CAP_DR1 == 0 { 623 warn!( 624 "Skipping codec {} due to lack of DR1 capability.", 625 codec_name 626 ); 627 return None; 628 } 629 630 Some((format, codec)) 631 }) 632 .collect(); 633 634 Self { codecs } 635 } 636 } 637 638 impl DecoderBackend for FfmpegDecoder { 639 type Session = FfmpegDecoderSession; 640 get_capabilities(&self) -> Capability641 fn get_capabilities(&self) -> Capability { 642 // The virtio device only supports NV12 for now it seems... 643 const SUPPORTED_OUTPUT_FORMATS: [Format; 1] = [Format::NV12]; 644 645 let mut in_formats = vec![]; 646 let mut profiles_map: BTreeMap<Format, Vec<Profile>> = Default::default(); 647 let mut levels: BTreeMap<Format, Vec<Level>> = Default::default(); 648 for (&format, codec) in &self.codecs { 649 let profile_iter = codec.profile_iter(); 650 let profiles = match format { 651 Format::H264 => { 652 // We only support Level 1.0 for H.264. 653 // TODO Do we? Why? 654 levels.insert(format, vec![Level::H264_1_0]); 655 656 profile_iter 657 .filter_map(|p| { 658 match p.profile() { 659 FF_PROFILE_H264_BASELINE => Some(Profile::H264Baseline), 660 FF_PROFILE_H264_MAIN => Some(Profile::H264Main), 661 FF_PROFILE_H264_EXTENDED => Some(Profile::H264Extended), 662 FF_PROFILE_H264_HIGH => Some(Profile::H264High), 663 FF_PROFILE_H264_HIGH_10 => Some(Profile::H264High10), 664 FF_PROFILE_H264_HIGH_422 => Some(Profile::H264High422), 665 FF_PROFILE_H264_HIGH_444_PREDICTIVE => { 666 Some(Profile::H264High444PredictiveProfile) 667 } 668 FF_PROFILE_H264_STEREO_HIGH => Some(Profile::H264StereoHigh), 669 FF_PROFILE_H264_MULTIVIEW_HIGH => Some(Profile::H264MultiviewHigh), 670 // TODO H264ScalableBaseline and H264ScalableHigh have no libav 671 // equivalents? 672 _ => None, 673 } 674 }) 675 .collect() 676 } 677 Format::VP8 => { 678 // FFmpeg has no VP8 profiles, for some reason... 679 vec![ 680 Profile::VP8Profile0, 681 Profile::VP8Profile1, 682 Profile::VP8Profile2, 683 Profile::VP8Profile3, 684 ] 685 } 686 Format::VP9 => profile_iter 687 .filter_map(|p| match p.profile() { 688 FF_PROFILE_VP9_0 => Some(Profile::VP9Profile0), 689 FF_PROFILE_VP9_1 => Some(Profile::VP9Profile1), 690 FF_PROFILE_VP9_2 => Some(Profile::VP9Profile2), 691 FF_PROFILE_VP9_3 => Some(Profile::VP9Profile3), 692 _ => None, 693 }) 694 .collect(), 695 Format::Hevc => profile_iter 696 .filter_map(|p| match p.profile() { 697 FF_PROFILE_HEVC_MAIN => Some(Profile::HevcMain), 698 FF_PROFILE_HEVC_MAIN_10 => Some(Profile::HevcMain10), 699 FF_PROFILE_HEVC_MAIN_STILL_PICTURE => Some(Profile::HevcMainStillPicture), 700 _ => None, 701 }) 702 .collect(), 703 _ => unreachable!("Unhandled format {:?}", format), 704 }; 705 706 profiles_map.insert(format, profiles); 707 708 in_formats.push(FormatDesc { 709 mask: !(u64::MAX << SUPPORTED_OUTPUT_FORMATS.len()), 710 format, 711 frame_formats: vec![FrameFormat { 712 // These frame sizes are arbitrary, but avcodec does not seem to have any 713 // specific restriction in that regard (or any way to query the supported 714 // resolutions). 715 width: FormatRange { 716 min: 64, 717 max: 16384, 718 step: 1, 719 }, 720 height: FormatRange { 721 min: 64, 722 max: 16384, 723 step: 1, 724 }, 725 bitrates: Default::default(), 726 }], 727 plane_align: max_buffer_alignment() as u32, 728 }); 729 } 730 731 // We support all output formats through the use of swscale(). 732 let out_formats = SUPPORTED_OUTPUT_FORMATS 733 .iter() 734 .map(|&format| FormatDesc { 735 mask: !(u64::MAX << in_formats.len()), 736 format, 737 frame_formats: vec![FrameFormat { 738 // These frame sizes are arbitrary, but avcodec does not seem to have any 739 // specific restriction in that regard (or any way to query the supported 740 // resolutions). 741 width: FormatRange { 742 min: 64, 743 max: 16384, 744 step: 1, 745 }, 746 height: FormatRange { 747 min: 64, 748 max: 16384, 749 step: 1, 750 }, 751 bitrates: Default::default(), 752 }], 753 plane_align: max_buffer_alignment() as u32, 754 }) 755 .collect::<Vec<_>>(); 756 757 Capability::new(in_formats, out_formats, profiles_map, levels) 758 } 759 new_session(&mut self, format: Format) -> VideoResult<Self::Session>760 fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> { 761 let codec = self.codecs.get(&format).ok_or(VideoError::InvalidFormat)?; 762 let context = codec 763 // TODO we should use a custom `get_buffer` function that renders directly into the 764 // target buffer if the output format is directly supported by libavcodec. Right now 765 // libavcodec is allocating its own frame buffers, which forces us to perform a copy. 766 .build_decoder() 767 .and_then(|b| b.build()) 768 .context("while creating new session") 769 .map_err(VideoError::BackendFailure)?; 770 Ok(FfmpegDecoderSession { 771 codec_jobs: Default::default(), 772 is_flushing: false, 773 state: SessionState::AwaitingInitialResolution, 774 event_queue: Arc::new( 775 EventQueue::new() 776 .context("while creating decoder session") 777 .map_err(VideoError::BackendFailure)? 778 .into(), 779 ), 780 context, 781 current_visible_res: (0, 0), 782 avframe: None, 783 }) 784 } 785 } 786 787 #[cfg(test)] 788 mod tests { 789 use super::super::tests::*; 790 use super::*; 791 792 #[test] test_get_capabilities()793 fn test_get_capabilities() { 794 let decoder = FfmpegDecoder::new(); 795 let caps = decoder.get_capabilities(); 796 assert!(!caps.input_formats().is_empty()); 797 assert!(!caps.output_formats().is_empty()); 798 } 799 800 #[test] test_decode_h264_guestmem_to_guestmem()801 fn test_decode_h264_guestmem_to_guestmem() { 802 decode_h264_generic( 803 &mut FfmpegDecoder::new(), 804 build_guest_mem_handle, 805 build_guest_mem_handle, 806 ); 807 } 808 809 // Decode using guest memory input and virtio object output buffers. 810 #[test] test_decode_h264_guestmem_to_object()811 fn test_decode_h264_guestmem_to_object() { 812 decode_h264_generic( 813 &mut FfmpegDecoder::new(), 814 build_guest_mem_handle, 815 build_object_handle, 816 ); 817 } 818 819 // Decode using virtio object input and guest memory output buffers. 820 #[test] test_decode_h264_object_to_guestmem()821 fn test_decode_h264_object_to_guestmem() { 822 decode_h264_generic( 823 &mut FfmpegDecoder::new(), 824 build_object_handle, 825 build_guest_mem_handle, 826 ); 827 } 828 829 // Decode using virtio object input and output buffers. 830 #[test] test_decode_h264_object_to_object()831 fn test_decode_h264_object_to_object() { 832 decode_h264_generic( 833 &mut FfmpegDecoder::new(), 834 build_object_handle, 835 build_object_handle, 836 ); 837 } 838 } 839