// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! This module implements a lightweight and safe decoder interface over `libavcodec`. It is5//! designed to concentrate all calls to unsafe methods in one place, while providing the same6//! low-level access as the libavcodec functions do.78use std::ffi::CStr;9use std::fmt::Debug;10use std::fmt::Display;11use std::marker::PhantomData;12use std::mem::ManuallyDrop;13use std::ops::Deref;1415use libc::c_char;16use libc::c_int;17use libc::c_void;18use thiserror::Error as ThisError;1920use super::*;21use crate::ffi::AVPictureType;2223/// An error returned by a low-level libavcodec function.24#[derive(Debug, ThisError)]25pub struct AvError(pub libc::c_int);2627impl AvError {28pub fn result(ret: c_int) -> Result<(), Self> {29if ret >= 0 {30Ok(())31} else {32Err(AvError(ret))33}34}35}3637impl Display for AvError {38fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {39let mut buffer = [0u8; 255];40let ret =41// SAFETY:42// Safe because we are passing valid bounds for the buffer.43unsafe { ffi::av_strerror(self.0, buffer.as_mut_ptr() as *mut c_char, buffer.len()) };44match ret {45ret if ret >= 0 => {46let end_of_string = buffer.iter().position(|i| *i == 0).unwrap_or(buffer.len());47let error_string = std::string::String::from_utf8_lossy(&buffer[..end_of_string]);48f.write_str(&error_string)49}50_ => f.write_fmt(format_args!("Unknown avcodec error {}", self.0)),51}52}53}5455/// Lightweight abstraction over libavcodec's `AVCodec` struct, allowing the query the capabilities56/// of supported codecs and opening a session to work with them.57///58/// `AVCodec` instances in libavcodec are all static, hence we can safely use a static reference59/// lifetime here.60pub struct AvCodec(&'static ffi::AVCodec);6162#[derive(Debug, ThisError)]63pub enum AvCodecOpenError {64#[error("failed to allocate AVContext object")]65ContextAllocation,66#[error("failed to open AVContext object")]67ContextOpen,68#[error("ContextBuilder variant does not match codec type")]69UnexpectedCodecType,70}7172/// Dimensions of a frame, used in AvCodecContext and AvFrame.73#[derive(Copy, Clone, Debug, PartialEq, Eq)]74pub struct Dimensions {75pub width: u32,76pub height: u32,77}7879impl AvCodec {80/// Returns whether the codec is a decoder.81pub fn is_decoder(&self) -> bool {82// SAFETY:83// Safe because `av_codec_is_decoder` is called on a valid static `AVCodec` reference.84(unsafe { ffi::av_codec_is_decoder(self.0) } != 0)85}8687/// Returns whether the codec is an encoder.88pub fn is_encoder(&self) -> bool {89// SAFETY:90// Safe because `av_codec_is_encoder` is called on a valid static `AVCodec` reference.91(unsafe { ffi::av_codec_is_encoder(self.0) } != 0)92}9394/// Returns the name of the codec.95pub fn name(&self) -> &'static str {96const INVALID_CODEC_STR: &str = "invalid codec";9798// SAFETY:99// Safe because `CStr::from_ptr` is called on a valid zero-terminated C string.100unsafe { CStr::from_ptr(self.0.name).to_str() }.unwrap_or(INVALID_CODEC_STR)101}102103/// Returns the capabilities of the codec, as a mask of AV_CODEC_CAP_* bits.104pub fn capabilities(&self) -> u32 {105self.0.capabilities as u32106}107108/// Returns an iterator over the profiles supported by this codec.109pub fn profile_iter(&self) -> AvProfileIterator {110AvProfileIterator(self.0.profiles)111}112113/// Returns an iterator over the pixel formats supported by this codec.114///115/// For a decoder, the returned array will likely be empty. This means that ffmpeg's native116/// pixel format (YUV420) will be used.117pub fn pixel_format_iter(&self) -> AvPixelFormatIterator {118AvPixelFormatIterator(self.0.pix_fmts)119}120121/// Get a builder for a encoder [`AvCodecContext`] using this codec.122pub fn build_encoder(&self) -> Result<EncoderContextBuilder, AvCodecOpenError> {123if !self.is_encoder() {124return Err(AvCodecOpenError::UnexpectedCodecType);125}126127Ok(EncoderContextBuilder {128codec: self.0,129context: self.alloc_context()?,130})131}132133/// Get a builder for a decoder [`AvCodecContext`] using this codec.134pub fn build_decoder(&self) -> Result<DecoderContextBuilder, AvCodecOpenError> {135if !self.is_decoder() {136return Err(AvCodecOpenError::UnexpectedCodecType);137}138139Ok(DecoderContextBuilder {140codec: self.0,141context: self.alloc_context()?,142})143}144145/// Internal helper for `build_decoder` to allocate an [`AvCodecContext`]. This needs to be146/// paired with a later call to [`AvCodecContext::init`].147fn alloc_context(&self) -> Result<AvCodecContext, AvCodecOpenError> {148// TODO(b:315859322): add safety doc string149#[allow(clippy::undocumented_unsafe_blocks)]150let context = unsafe { ffi::avcodec_alloc_context3(self.0).as_mut() }151.ok_or(AvCodecOpenError::ContextAllocation)?;152153Ok(AvCodecContext(context))154}155}156157/// A builder to create a [`AvCodecContext`] suitable for decoding.158// This struct wraps an AvCodecContext directly, but the only way it can be taken out is to call159// `build()`, which finalizes the context and prevent further modification to the callback, etc.160pub struct DecoderContextBuilder {161codec: *const ffi::AVCodec,162context: AvCodecContext,163}164165impl DecoderContextBuilder {166/// Set a custom callback that provides output buffers.167///168/// `get_buffer2` is a function that decides which buffer is used to render a frame (see169/// libavcodec's documentation for `get_buffer2` for more details). If provided, this function170/// must be thread-safe.171/// `opaque` is a pointer that will be passed as first argument to `get_buffer2` when it is172/// called.173pub fn set_get_buffer_2(174&mut self,175get_buffer2: unsafe extern "C" fn(*mut ffi::AVCodecContext, *mut ffi::AVFrame, i32) -> i32,176opaque: *mut libc::c_void,177) {178// SAFETY:179// Safe because self.context.0 is a pointer to a live AVCodecContext allocation.180let context = unsafe { &mut *(self.context.0) };181context.get_buffer2 = Some(get_buffer2);182context.opaque = opaque;183}184185/// Build a decoder AvCodecContext from the configured options.186pub fn build(mut self) -> Result<AvCodecContext, AvCodecOpenError> {187self.context.init(self.codec)?;188Ok(self.context)189}190}191192/// A builder to create a [`AvCodecContext`] suitable for encoding.193// This struct wraps an AvCodecContext directly, but the only way it can be taken out is to call194// `build()`, which finalizes the context and prevent further modification to the callback, etc.195pub struct EncoderContextBuilder {196codec: *const ffi::AVCodec,197context: AvCodecContext,198}199200impl EncoderContextBuilder {201/// Set the width of input frames for this encoding context.202pub fn set_dimensions(&mut self, dimensions: Dimensions) {203// TODO(b:315859322): add safety doc string204#[allow(clippy::undocumented_unsafe_blocks)]205let context = unsafe { &mut *(self.context.0) };206context.width = dimensions.width as _;207context.height = dimensions.height as _;208}209210/// Set the time base for this encoding context.211pub fn set_time_base(&mut self, time_base: ffi::AVRational) {212// TODO(b:315859322): add safety doc string213#[allow(clippy::undocumented_unsafe_blocks)]214let context = unsafe { &mut *(self.context.0) };215context.time_base = time_base;216}217218/// Set the input pixel format for this encoding context.219pub fn set_pix_fmt(&mut self, fmt: AvPixelFormat) {220// TODO(b:315859322): add safety doc string221#[allow(clippy::undocumented_unsafe_blocks)]222let context = unsafe { &mut *(self.context.0) };223context.pix_fmt = fmt.pix_fmt();224}225226/// Build a encoder AvCodecContext from the configured options.227pub fn build(mut self) -> Result<AvCodecContext, AvCodecOpenError> {228self.context.init(self.codec)?;229Ok(self.context)230}231}232233impl Default for AvCodecIterator {234fn default() -> Self {235Self::new()236}237}238239/// Lightweight abstraction over libavcodec's `av_codec_iterate` function that can be used to240/// enumerate all the supported codecs.241pub struct AvCodecIterator(*mut libc::c_void);242243impl AvCodecIterator {244pub fn new() -> Self {245Self(std::ptr::null_mut())246}247}248249impl Iterator for AvCodecIterator {250type Item = AvCodec;251252fn next(&mut self) -> Option<Self::Item> {253// SAFETY:254// Safe because our pointer was initialized to `NULL` and we only use it with255// `av_codec_iterate`, which will update it to a valid value.256unsafe { ffi::av_codec_iterate(&mut self.0 as *mut *mut libc::c_void).as_ref() }257.map(AvCodec)258}259}260261/// Simple wrapper over `AVProfile` that provides helpful methods.262pub struct AvProfile(&'static ffi::AVProfile);263264impl AvProfile {265/// Return the profile id, which can be matched against AV_PROFILE_*.266pub fn profile(&self) -> u32 {267self.0.profile as u32268}269270/// Return the name of this profile.271pub fn name(&self) -> &'static str {272const INVALID_PROFILE_STR: &str = "invalid profile";273274// SAFETY:275// Safe because `CStr::from_ptr` is called on a valid zero-terminated C string.276unsafe { CStr::from_ptr(self.0.name).to_str() }.unwrap_or(INVALID_PROFILE_STR)277}278}279280impl Display for AvProfile {281fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {282f.write_str(self.name())283}284}285286impl Debug for AvProfile {287fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {288Display::fmt(self, f)289}290}291292/// Lightweight abstraction over the array of supported profiles for a given codec.293pub struct AvProfileIterator(*const ffi::AVProfile);294295impl Iterator for AvProfileIterator {296type Item = AvProfile;297298fn next(&mut self) -> Option<Self::Item> {299// SAFETY:300// Safe because the contract of `new` stipulates we have received a valid `AVCodec`301// reference, thus the `profiles` pointer must either be NULL or point to a valid array302// or `VAProfile`s.303match unsafe { self.0.as_ref() } {304None => None,305Some(profile) => {306match profile.profile {307ffi::AV_PROFILE_UNKNOWN => None,308_ => {309// SAFETY:310// Safe because we have been initialized to a static, valid profiles array311// which is terminated by AV_PROFILE_UNKNOWN.312self.0 = unsafe { self.0.offset(1) };313Some(AvProfile(profile))314}315}316}317}318}319}320321#[derive(Clone, Copy)]322/// Simple wrapper over `AVPixelFormat` that provides helpful methods.323pub struct AvPixelFormat(ffi::AVPixelFormat);324325impl AvPixelFormat {326/// Return the name of this pixel format.327pub fn name(&self) -> &'static str {328const INVALID_FORMAT_STR: &str = "invalid pixel format";329330// SAFETY:331// Safe because `av_get_pix_fmt_name` returns either NULL or a valid C string.332let pix_fmt_name = unsafe { ffi::av_get_pix_fmt_name(self.0) };333// SAFETY:334// Safe because `pix_fmt_name` is a valid pointer to a C string.335match unsafe {336pix_fmt_name337.as_ref()338.and_then(|s| CStr::from_ptr(s).to_str().ok())339} {340None => INVALID_FORMAT_STR,341Some(string) => string,342}343}344345/// Return the avcodec profile id, which can be matched against AV_PIX_FMT_*.346///347/// Note that this is **not** the same as a fourcc.348pub fn pix_fmt(&self) -> ffi::AVPixelFormat {349self.0350}351352/// Return the fourcc of the pixel format, or a series of zeros if its fourcc is unknown.353pub fn fourcc(&self) -> [u8; 4] {354// SAFETY:355// Safe because `avcodec_pix_fmt_to_codec_tag` does not take any pointer as input and356// handles any value passed as argument.357unsafe { ffi::avcodec_pix_fmt_to_codec_tag(self.0) }.to_le_bytes()358}359360/// Given the width and plane index, returns the line size (data pointer increment per row) in361/// bytes.362pub fn line_size(&self, width: u32, plane: usize) -> Result<usize, AvError> {363av_image_line_size(*self, width, plane)364}365366/// Given an iterator of line sizes and height, return the size required for each plane's buffer367/// in bytes.368pub fn plane_sizes<I: IntoIterator<Item = u32>>(369&self,370linesizes: I,371height: u32,372) -> Result<Vec<usize>, AvError> {373av_image_plane_sizes(*self, linesizes, height)374}375}376377#[derive(Debug)]378pub struct FromAVPixelFormatError(());379380impl TryFrom<ffi::AVPixelFormat> for AvPixelFormat {381type Error = FromAVPixelFormatError;382383fn try_from(value: ffi::AVPixelFormat) -> Result<Self, Self::Error> {384if value > ffi::AVPixelFormat_AV_PIX_FMT_NONE && value < ffi::AVPixelFormat_AV_PIX_FMT_NB {385Ok(AvPixelFormat(value))386} else {387Err(FromAVPixelFormatError(()))388}389}390}391392impl Display for AvPixelFormat {393fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {394f.write_str(self.name())395}396}397398impl Debug for AvPixelFormat {399fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {400let fourcc = self.fourcc();401f.write_fmt(format_args!(402"{}{}{}{}",403fourcc[0] as char, fourcc[1] as char, fourcc[2] as char, fourcc[3] as char404))405}406}407408/// Lightweight abstraction over the array of supported pixel formats for a given codec.409pub struct AvPixelFormatIterator(*const ffi::AVPixelFormat);410411impl Iterator for AvPixelFormatIterator {412type Item = AvPixelFormat;413414fn next(&mut self) -> Option<Self::Item> {415// SAFETY:416// Safe because the contract of `AvCodec::new` and `AvCodec::pixel_format_iter` guarantees417// that we have been built from a valid `AVCodec` reference, which `pix_fmts` pointer418// must either be NULL or point to a valid array or `VAPixelFormat`s.419match unsafe { self.0.as_ref() } {420None => None,421Some(&pixfmt) => {422match pixfmt {423// Array of pixel formats is terminated by AV_PIX_FMT_NONE.424ffi::AVPixelFormat_AV_PIX_FMT_NONE => None,425_ => {426// SAFETY:427// Safe because we have been initialized to a static, valid profiles array428// which is terminated by AV_PIX_FMT_NONE.429self.0 = unsafe { self.0.offset(1) };430Some(AvPixelFormat(pixfmt))431}432}433}434}435}436}437438/// A codec context from which decoding can be performed.439pub struct AvCodecContext(*mut ffi::AVCodecContext);440441impl Drop for AvCodecContext {442fn drop(&mut self) {443// SAFETY:444// Safe because our context member is properly allocated and owned by us.445// Note: `avcodec_open2` might not have been called in case we're wrapped by a446// `DecoderContextBuilder` but avcodec_free_context works on both opened and closed447// contexts.448unsafe { ffi::avcodec_free_context(&mut self.0) };449}450}451452impl AsRef<ffi::AVCodecContext> for AvCodecContext {453fn as_ref(&self) -> &ffi::AVCodecContext {454// SAFETY:455// Safe because our context member is properly initialized and fully owned by us.456unsafe { &*self.0 }457}458}459460pub enum TryReceiveResult {461Received,462TryAgain,463FlushCompleted,464}465466impl AvCodecContext {467/// Internal helper for [`DecoderContextBuilder`] to initialize the context.468fn init(&mut self, codec: *const ffi::AVCodec) -> Result<(), AvCodecOpenError> {469// SAFETY:470// Safe because `codec` is a valid static AVCodec reference, and `self.0` is a valid471// AVCodecContext allocation.472if unsafe { ffi::avcodec_open2(self.0, codec, std::ptr::null_mut()) } < 0 {473return Err(AvCodecOpenError::ContextOpen);474}475476Ok(())477}478479/// Send a packet to be decoded by the codec.480///481/// Returns `true` if the packet has been accepted and will be decoded, `false` if the codec can482/// not accept frames at the moment - in this case `try_receive_frame` must be called before483/// the packet can be submitted again.484///485/// Error codes are the same as those returned by `avcodec_send_packet` with the exception of486/// EAGAIN which is converted into `Ok(false)` as it is not actually an error.487pub fn try_send_packet(&mut self, packet: &AvPacket) -> Result<bool, AvError> {488// SAFETY:489// Safe because the context is valid through the life of this object, and `packet`'s490// lifetime properties ensures its memory area is readable.491match unsafe { ffi::avcodec_send_packet(self.0, &packet.packet) } {492AVERROR_EAGAIN => Ok(false),493ret if ret >= 0 => Ok(true),494err => Err(AvError(err)),495}496}497498/// Attempt to write a decoded frame in `frame` if the codec has enough data to do so.499///500/// Returned `Received` if `frame` has been filled with the next decoded frame, `TryAgain` if501/// no frame could be returned at that time (in which case `try_send_packet` should be called to502/// submit more input to decode), or `FlushCompleted` to signal that a previous flush triggered503/// by calling the `flush` method has completed.504///505/// Error codes are the same as those returned by `avcodec_receive_frame` with the exception of506/// EAGAIN and EOF which are handled as `TryAgain` and `FlushCompleted` respectively.507pub fn try_receive_frame(&mut self, frame: &mut AvFrame) -> Result<TryReceiveResult, AvError> {508// SAFETY:509// Safe because the context is valid through the life of this object, and `avframe` is510// guaranteed to contain a properly initialized frame.511match unsafe { ffi::avcodec_receive_frame(self.0, frame.0) } {512AVERROR_EAGAIN => Ok(TryReceiveResult::TryAgain),513AVERROR_EOF => Ok(TryReceiveResult::FlushCompleted),514ret if ret >= 0 => Ok(TryReceiveResult::Received),515err => Err(AvError(err)),516}517}518519/// Send a frame to be encoded by the codec.520///521/// Returns `true` if the frame has been accepted and will be encoded, `false` if the codec can522/// not accept input at the moment - in this case `try_receive_frame` must be called before523/// the frame can be submitted again.524///525/// Error codes are the same as those returned by `avcodec_send_frame` with the exception of526/// EAGAIN which is converted into `Ok(false)` as it is not actually an error.527pub fn try_send_frame(&mut self, frame: &AvFrame) -> Result<bool, AvError> {528// TODO(b:315859322): add safety doc string529#[allow(clippy::undocumented_unsafe_blocks)]530match unsafe { ffi::avcodec_send_frame(self.0, frame.0 as *const _) } {531AVERROR_EAGAIN => Ok(false),532ret if ret >= 0 => Ok(true),533err => Err(AvError(err)),534}535}536537/// Attempt to write an encoded frame in `packet` if the codec has enough data to do so.538///539/// Returned `Received` if `packet` has been filled with encoded data, `TryAgain` if540/// no packet could be returned at that time (in which case `try_send_frame` should be called to541/// submit more input to decode), or `FlushCompleted` to signal that a previous flush triggered542/// by calling the `flush` method has completed.543///544/// Error codes are the same as those returned by `avcodec_receive_packet` with the exception of545/// EAGAIN and EOF which are handled as `TryAgain` and `FlushCompleted` respectively.546pub fn try_receive_packet(547&mut self,548packet: &mut AvPacket,549) -> Result<TryReceiveResult, AvError> {550// SAFETY:551// Safe because the context is valid through the life of this object, and `avframe` is552// guaranteed to contain a properly initialized frame.553match unsafe { ffi::avcodec_receive_packet(self.0, &mut packet.packet) } {554AVERROR_EAGAIN => Ok(TryReceiveResult::TryAgain),555AVERROR_EOF => Ok(TryReceiveResult::FlushCompleted),556ret if ret >= 0 => Ok(TryReceiveResult::Received),557err => Err(AvError(err)),558}559}560561/// Reset the internal codec state/flush internal buffers.562/// Should be called e.g. when seeking or switching to a different stream.563pub fn reset(&mut self) {564// SAFETY:565// Safe because the context is valid through the life of this object.566unsafe { ffi::avcodec_flush_buffers(self.0) }567}568569/// Ask the context to start flushing, i.e. to process all pending input packets and produce570/// frames for them.571///572/// The flush process is complete when `try_receive_frame` returns `FlushCompleted`,573pub fn flush_decoder(&mut self) -> Result<(), AvError> {574// SAFETY:575// Safe because the context is valid through the life of this object.576AvError::result(unsafe { ffi::avcodec_send_packet(self.0, std::ptr::null()) })577}578579/// Ask the context to start flushing, i.e. to process all pending input frames and produce580/// packets for them.581///582/// The flush process is complete when `try_receive_packet` returns `FlushCompleted`,583pub fn flush_encoder(&mut self) -> Result<(), AvError> {584// SAFETY:585// Safe because the context is valid through the life of this object.586AvError::result(unsafe { ffi::avcodec_send_frame(self.0, std::ptr::null()) })587}588589/// Set the time base for this context.590pub fn set_time_base(&mut self, time_base: AVRational) {591// TODO(b:315859322): add safety doc string592#[allow(clippy::undocumented_unsafe_blocks)]593let context = unsafe { &mut *(self.0) };594context.time_base = time_base;595}596597/// Set the bit rate for this context.598pub fn set_bit_rate(&mut self, bit_rate: u64) {599// TODO(b:315859322): add safety doc string600#[allow(clippy::undocumented_unsafe_blocks)]601let context = unsafe { &mut *(self.0) };602context.bit_rate = bit_rate as _;603}604605/// Set the max bit rate (rc_max_rate) for this context.606pub fn set_max_bit_rate(&mut self, bit_rate: u64) {607// TODO(b:315859322): add safety doc string608#[allow(clippy::undocumented_unsafe_blocks)]609let context = unsafe { &mut *(self.0) };610context.rc_max_rate = bit_rate as _;611}612}613614/// Trait for types that can be used as data provider for a `AVBuffer`.615///616/// `AVBuffer` is an owned buffer type, so all the type needs to do is being able to provide a617/// stable pointer to its own data as well as its length. Implementors need to be sendable across618/// threads because avcodec is allowed to use threads in its codec implementations.619pub trait AvBufferSource: Send {620fn as_ptr(&self) -> *const u8;621fn as_mut_ptr(&mut self) -> *mut u8 {622self.as_ptr() as *mut u8623}624fn len(&self) -> usize;625fn is_empty(&self) -> bool;626}627628/// Wrapper around `AVBuffer` and `AVBufferRef`.629///630/// libavcodec can manage its own memory for input and output data. Doing so implies a transparent631/// copy of user-provided data (packets or frames) from and to this memory, which is wasteful.632///633/// This copy can be avoided by explicitly providing our own buffers to libavcodec using634/// `AVBufferRef`. Doing so means that the lifetime of these buffers becomes managed by avcodec.635/// This struct helps make this process safe by taking full ownership of an `AvBufferSource` and636/// dropping it when libavcodec is done with it.637pub struct AvBuffer(*mut ffi::AVBufferRef);638639impl AvBuffer {640/// Create a new `AvBuffer` from an `AvBufferSource`.641///642/// Ownership of `source` is transferred to libavcodec, which will drop it when the number of643/// references to this buffer reaches zero.644///645/// Returns `None` if the buffer could not be created due to an error in libavcodec.646pub fn new<D: AvBufferSource + 'static>(source: D) -> Option<Self> {647// Move storage to the heap so we find it at the same place in `avbuffer_free`648let mut storage = Box::new(source);649650extern "C" fn avbuffer_free<D>(opaque: *mut c_void, _data: *mut u8) {651// SAFETY:652// Safe because `opaque` has been created from `Box::into_raw`. `storage` will be653// dropped immediately which will release any resources held by the storage.654let _ = unsafe { Box::from_raw(opaque as *mut D) };655}656657// SAFETY:658// Safe because storage points to valid data throughout the lifetime of AVBuffer and we are659// checking the return value against NULL, which signals an error.660Some(Self(unsafe {661ffi::av_buffer_create(662storage.as_mut_ptr(),663storage.len(),664Some(avbuffer_free::<D>),665Box::into_raw(storage) as *mut c_void,6660,667)668.as_mut()?669}))670}671672/// Return a slice to the data contained in this buffer.673pub fn as_mut_slice(&mut self) -> &mut [u8] {674// SAFETY:675// Safe because the data has been initialized from valid storage in the constructor.676unsafe { std::slice::from_raw_parts_mut((*self.0).data, (*self.0).size) }677}678679/// Consumes the `AVBuffer`, returning a `AVBufferRef` that can be used in `AVFrame`, `AVPacket`680/// and others.681///682/// After calling, the caller is responsible for unref-ing the returned AVBufferRef, either683/// directly or through one of the automatic management facilities in `AVFrame`, `AVPacket` or684/// others.685pub fn into_raw(self) -> *mut ffi::AVBufferRef {686ManuallyDrop::new(self).0687}688}689690impl Drop for AvBuffer {691fn drop(&mut self) {692// SAFETY:693// Safe because `self.0` is a valid pointer to an AVBufferRef.694unsafe { ffi::av_buffer_unref(&mut self.0) };695}696}697698/// An encoded input packet that can be submitted to `AvCodecContext::try_send_packet`.699pub struct AvPacket<'a> {700packet: ffi::AVPacket,701_buffer_data: PhantomData<&'a ()>,702}703704impl Drop for AvPacket<'_> {705fn drop(&mut self) {706// SAFETY:707// Safe because `self.packet` is a valid `AVPacket` instance.708unsafe {709ffi::av_packet_unref(&mut self.packet);710}711}712}713714impl AsRef<ffi::AVPacket> for AvPacket<'_> {715fn as_ref(&self) -> &ffi::AVPacket {716&self.packet717}718}719720impl<'a> AvPacket<'a> {721/// Create an empty AvPacket without buffers.722///723/// This packet should be only used with an encoder; in which case the encoder will724/// automatically allocate a buffer of appropriate size and store it inside this `AvPacket`.725pub fn empty() -> Self {726Self {727packet: ffi::AVPacket {728pts: AV_NOPTS_VALUE as i64,729dts: AV_NOPTS_VALUE as i64,730pos: -1,731// SAFETY:732// Safe because all the other elements of this struct can be zeroed.733..unsafe { std::mem::zeroed() }734},735_buffer_data: PhantomData,736}737}738739/// Create a new AvPacket that borrows the `input_data`.740///741/// The returned `AvPacket` will hold a reference to `input_data`, meaning that libavcodec might742/// perform a copy from/to it.743pub fn new<T: AvBufferSource>(pts: i64, input_data: &'a mut T) -> Self {744Self {745packet: ffi::AVPacket {746buf: std::ptr::null_mut(),747pts,748dts: AV_NOPTS_VALUE as i64,749data: input_data.as_mut_ptr(),750size: input_data.len() as c_int,751side_data: std::ptr::null_mut(),752pos: -1,753// SAFETY:754// Safe because all the other elements of this struct can be zeroed.755..unsafe { std::mem::zeroed() }756},757_buffer_data: PhantomData,758}759}760761/// Create a new AvPacket that owns the `av_buffer`.762///763/// The returned `AvPacket` will have a `'static` lifetime and will keep `input_data` alive for764/// as long as libavcodec needs it.765pub fn new_owned(pts: i64, mut av_buffer: AvBuffer) -> Self {766let data_slice = av_buffer.as_mut_slice();767let data = data_slice.as_mut_ptr();768let size = data_slice.len() as i32;769770Self {771packet: ffi::AVPacket {772buf: av_buffer.into_raw(),773pts,774dts: AV_NOPTS_VALUE as i64,775data,776size,777side_data: std::ptr::null_mut(),778pos: -1,779// SAFETY:780// Safe because all the other elements of this struct can be zeroed.781..unsafe { std::mem::zeroed() }782},783_buffer_data: PhantomData,784}785}786}787788/// An owned AVFrame, i.e. one decoded frame from libavcodec that can be converted into a789/// destination buffer.790pub struct AvFrame(*mut ffi::AVFrame);791792/// A builder for AVFrame that allows specifying buffers and image metadata.793pub struct AvFrameBuilder(AvFrame);794795/// A descriptor describing a subslice of `buffers` in [`AvFrameBuilder::build_owned`] that796/// represents a plane's image data.797pub struct PlaneDescriptor {798/// The index within `buffers`.799pub buffer_index: usize,800/// The offset from the start of `buffers[buffer_index]`.801pub offset: usize,802/// The increment of data pointer in bytes per row of the plane.803pub stride: usize,804}805806#[derive(Debug, ThisError)]807pub enum AvFrameError {808#[error("failed to allocate AVFrame object")]809FrameAllocationFailed,810#[error("dimension is negative or too large")]811DimensionOverflow,812#[error("a row does not fit in the specified stride")]813InvalidStride,814#[error("buffer index out of range")]815BufferOutOfRange,816#[error("specified dimensions overflow the buffer size")]817BufferTooSmall,818#[error("plane reference to buffer alias each other")]819BufferAlias,820#[error("error while calling libavcodec")]821AvError(#[from] AvError),822}823824impl AvFrame {825/// Create a new AvFrame. The frame's parameters and backing memory will be assigned when it is826/// decoded into.827pub fn new() -> Result<Self, AvFrameError> {828Ok(Self(829// SAFETY:830// Safe because `av_frame_alloc` does not take any input.831unsafe { ffi::av_frame_alloc().as_mut() }.ok_or(AvFrameError::FrameAllocationFailed)?,832))833}834835/// Create a new AvFrame builder that allows setting the frame's parameters and backing memory836/// through its methods.837pub fn builder() -> Result<AvFrameBuilder, AvFrameError> {838AvFrame::new().map(AvFrameBuilder)839}840841/// Return the frame's width and height.842pub fn dimensions(&self) -> Dimensions {843Dimensions {844width: self.as_ref().width as _,845height: self.as_ref().height as _,846}847}848849/// Return the frame's pixel format.850pub fn format(&self) -> AvPixelFormat {851AvPixelFormat(self.as_ref().format)852}853854/// Set the picture type (I-frame, P-frame etc.) on this frame.855pub fn set_pict_type(&mut self, ty: AVPictureType) {856// SAFETY:857// Safe because self.0 is a valid AVFrame reference.858unsafe {859(*self.0).pict_type = ty;860}861}862863/// Set the presentation timestamp (PTS) of this frame.864pub fn set_pts(&mut self, ts: i64) {865// SAFETY:866// Safe because self.0 is a valid AVFrame reference.867unsafe {868(*self.0).pts = ts;869}870}871872/// Query if this AvFrame is writable, i.e. it is refcounted and the refcounts are 1.873pub fn is_writable(&self) -> bool {874// SAFETY:875// Safe because self.0 is a valid AVFrame reference.876unsafe { ffi::av_frame_is_writable(self.0) != 0 }877}878879/// If the frame is not writable already (see [`is_writable`]), make a copy of its buffer to880/// make it writable.881///882/// [`is_writable`]: AvFrame::is_writable883pub fn make_writable(&mut self) -> Result<(), AvFrameError> {884// SAFETY:885// Safe because self.0 is a valid AVFrame reference.886AvError::result(unsafe { ffi::av_frame_make_writable(self.0) }).map_err(Into::into)887}888}889890impl AvFrameBuilder {891/// Set the frame's width and height.892///893/// The dimensions must not be greater than `i32::MAX`.894pub fn set_dimensions(&mut self, dimensions: Dimensions) -> Result<(), AvFrameError> {895// SAFETY:896// Safe because self.0 is a valid AVFrame instance and width and height are in range.897unsafe {898(*self.0 .0).width = dimensions899.width900.try_into()901.map_err(|_| AvFrameError::DimensionOverflow)?;902(*self.0 .0).height = dimensions903.height904.try_into()905.map_err(|_| AvFrameError::DimensionOverflow)?;906}907Ok(())908}909910/// Set the frame's format.911pub fn set_format(&mut self, format: AvPixelFormat) -> Result<(), AvFrameError> {912// SAFETY:913// Safe because self.0 is a valid AVFrame instance and format is a valid pixel format.914unsafe {915(*self.0 .0).format = format.pix_fmt();916}917Ok(())918}919920/// Build an AvFrame from iterators of [`AvBuffer`]s and subslice of buffers describing the921/// planes.922///923/// The frame will own the `buffers`.924///925/// This function checks that:926/// - Each plane fits inside the bounds of the associated buffer.927/// - Different planes do not overlap each other's buffer slice. In this check, all planes are928/// assumed to be potentially mutable, regardless of whether the AvFrame is actually used for929/// read or write access. Aliasing reference to the same buffer will be rejected, since it can930/// potentially allow routines to overwrite each931// other's result.932/// An exception to this is when the same buffer is passed multiple times in `buffers`. In933/// this case, each buffer is treated as a different buffer. Since clones have to be made to934/// be passed multiple times in `buffers`, the frame will not be considered [writable]. Hence935/// aliasing is safe in this case, but the caller is required to explicit opt-in to this936/// read-only handling by passing clones of the buffer into `buffers` and have a different937/// buffer index for each plane combination that could overlap in their range.938///939/// [writable]: AvFrame::is_writable940pub fn build_owned<941BI: IntoIterator<Item = AvBuffer>,942PI: IntoIterator<Item = PlaneDescriptor>,943>(944self,945buffers: BI,946planes: PI,947) -> Result<AvFrame, AvFrameError> {948let mut buffers: Vec<_> = buffers.into_iter().collect();949let planes: Vec<_> = planes.into_iter().collect();950let format = self.0.format();951let plane_sizes = format.plane_sizes(952planes.iter().map(|x| x.stride as u32),953self.0.dimensions().height,954)?;955let mut ranges = vec![];956957for (958plane,959PlaneDescriptor {960buffer_index,961offset,962stride,963},964) in planes.into_iter().enumerate()965{966if buffer_index > buffers.len() {967return Err(AvFrameError::BufferOutOfRange);968}969let end = offset + plane_sizes[plane];970if end > buffers[buffer_index].as_mut_slice().len() {971return Err(AvFrameError::BufferTooSmall);972}973if stride < format.line_size(self.0.dimensions().width, plane)? {974return Err(AvFrameError::InvalidStride);975}976// TODO(b:315859322): add safety doc string977#[allow(clippy::undocumented_unsafe_blocks)]978unsafe {979(*self.0 .0).data[plane] =980buffers[buffer_index].as_mut_slice()[offset..].as_mut_ptr();981(*self.0 .0).linesize[plane] = stride as c_int;982}983ranges.push((buffer_index, offset, end));984}985986// Check for range overlaps.987// See function documentation for the exact rule and reasoning.988ranges.sort_unstable();989for pair in ranges.windows(2) {990// (buffer_index, start, end)991let (b0, _s0, e0) = pair[0];992let (b1, s1, _e1) = pair[1];993994if b0 != b1 {995continue;996}997// Note that s0 <= s1 always holds, so we only need to check998// that the start of the second range is before the end of the first range.999if s1 < e0 {1000return Err(AvFrameError::BufferAlias);1001}1002}10031004for (i, buf) in buffers.into_iter().enumerate() {1005// SAFETY:1006// Safe because self.0 is a valid AVFrame instance and buffers contains valid AvBuffers.1007unsafe {1008(*self.0 .0).buf[i] = buf.into_raw();1009}1010}1011Ok(self.0)1012}1013}10141015impl AsRef<ffi::AVFrame> for AvFrame {1016fn as_ref(&self) -> &ffi::AVFrame {1017// SAFETY:1018// Safe because the AVFrame has been properly initialized during construction.1019unsafe { &*self.0 }1020}1021}10221023impl Deref for AvFrame {1024type Target = ffi::AVFrame;10251026fn deref(&self) -> &Self::Target {1027// SAFETY:1028// Safe because the AVFrame has been properly initialized during construction.1029unsafe { self.0.as_ref().unwrap() }1030}1031}10321033impl Drop for AvFrame {1034fn drop(&mut self) {1035// SAFETY:1036// Safe because the AVFrame is valid through the life of this object and fully owned by us.1037unsafe { ffi::av_frame_free(&mut self.0) };1038}1039}10401041#[cfg(test)]1042mod tests {1043use std::sync::atomic::AtomicBool;1044use std::sync::atomic::Ordering;1045use std::sync::Arc;10461047use super::*;10481049#[test]1050fn test_averror() {1051// Just test that the error is wrapper properly. The bindings test module already checks1052// that the error bindings correspond to the right ffmpeg errors.1053let averror = AvError(AVERROR_EOF);1054let msg = format!("{averror}");1055assert_eq!(msg, "End of file");10561057let averror = AvError(0);1058let msg = format!("{averror}");1059assert_eq!(msg, "Success");10601061let averror = AvError(10);1062let msg = format!("{averror}");1063assert_eq!(msg, "Unknown avcodec error 10");1064}10651066// Test that the AVPacket wrapper frees the owned AVBuffer on drop.1067#[test]1068fn test_avpacket_drop() {1069struct DropTestBufferSource {1070dropped: Arc<AtomicBool>,1071}1072impl Drop for DropTestBufferSource {1073fn drop(&mut self) {1074self.dropped.store(true, Ordering::SeqCst);1075}1076}1077impl AvBufferSource for DropTestBufferSource {1078fn as_ptr(&self) -> *const u8 {1079[].as_ptr()1080}10811082fn len(&self) -> usize {108301084}10851086fn is_empty(&self) -> bool {1087true1088}1089}10901091let dropped = Arc::new(AtomicBool::new(false));10921093let pkt = AvPacket::new_owned(10940,1095AvBuffer::new(DropTestBufferSource {1096dropped: dropped.clone(),1097})1098.unwrap(),1099);1100assert!(!dropped.load(Ordering::SeqCst));1101drop(pkt);1102assert!(dropped.load(Ordering::SeqCst));1103}1104}110511061107