Skip to content

Commit

Permalink
Revise transcode_aac example and various updates (#147)
Browse files Browse the repository at this point in the history
* More methods for `AVAudioFifo`, Remove unused error variants

* Make `SwrContext::convert` accept raw data by default, Remove `SwrContext::convert_raw`

* Add `pkt_timebase` setter for `AVCodecContext`

* Make `AVAudioFifo::write` returns nothing on success

* Add `AVFrame::get_buffer`

* Change `AVSamples` from vec to boxed slice

* Remove unnecessary error variant
  • Loading branch information
ldm0 authored Jan 23, 2024
1 parent 0133b51 commit d816f71
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 180 deletions.
1 change: 1 addition & 0 deletions src/avcodec/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ settable!(AVCodecContext {
sample_aspect_ratio: AVRational,
pix_fmt: i32,
time_base: AVRational,
pkt_timebase: AVRational,
sample_rate: i32,
channels: i32,
sample_fmt: i32,
Expand Down
6 changes: 2 additions & 4 deletions src/avformat/avformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl AVFormatContextInput {
match unsafe { ffi::av_read_frame(self.as_mut_ptr(), packet.as_mut_ptr()) }.upgrade() {
Ok(_) => Ok(Some(packet)),
Err(ffi::AVERROR_EOF) => Ok(None),
Err(x) => Err(RsmpegError::ReadFrameError(x)),
Err(x) => Err(x)?,
}
}

Expand Down Expand Up @@ -340,9 +340,7 @@ impl AVFormatContextOutput {
/// libavformat to handle the interleaving should call
/// [`Self::interleaved_write_frame()`] instead of this function.
pub fn write_frame(&mut self, packet: &mut AVPacket) -> Result<()> {
unsafe { ffi::av_write_frame(self.as_mut_ptr(), packet.as_mut_ptr()) }
.upgrade()
.map_err(RsmpegError::WriteFrameError)?;
unsafe { ffi::av_write_frame(self.as_mut_ptr(), packet.as_mut_ptr()) }.upgrade()?;
Ok(())
}

Expand Down
124 changes: 84 additions & 40 deletions src/avutil/audio_fifo.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,25 @@
use crate::{error::*, ffi, shared::*};
use std::ops::Drop;
wrap!(AVAudioFifo: ffi::AVAudioFifo);

wrap!(
/// Context for an Audio FIFO Buffer.
///
/// - Operates at the sample level rather than the byte level.
/// - Supports multiple channels with either planar or packed sample format.
/// - Automatic reallocation when writing to a full buffer.
AVAudioFifo: ffi::AVAudioFifo
);

impl AVAudioFifo {
/// Allocate an AVAudioFifo.
pub fn new(sample_fmt: ffi::AVSampleFormat, channels: i32, nb_samples: i32) -> Self {
let fifo = unsafe { ffi::av_audio_fifo_alloc(sample_fmt, channels, nb_samples) }
.upgrade()
.unwrap();
unsafe { Self::from_raw(fifo) }
}

/// Get the current number of samples in the [`AVAudioFifo`] available for
/// reading.
pub fn size(&self) -> i32 {
unsafe {
// function doesn't modify self, casting safe
ffi::av_audio_fifo_size(self.as_ptr() as *mut _)
}
}

/// Get the current number of samples in the [`AVAudioFifo`] available for
/// writing.
pub fn space(&self) -> i32 {
unsafe {
// function doesn't modify self, casting safe
ffi::av_audio_fifo_space(self.as_ptr() as *mut _)
}
}

pub fn reset(&mut self) {
unsafe { ffi::av_audio_fifo_reset(self.as_mut_ptr()) }
}

pub fn drain(&mut self, nb_samples: i32) {
// FFI function only error when the nb_samples is negative.
unsafe { ffi::av_audio_fifo_drain(self.as_mut_ptr(), nb_samples) }
.upgrade()
.unwrap();
}

/// Reallocate an AVAudioFifo.
pub fn realloc(&mut self, nb_samples: i32) {
// Almost only panic on no memory, in other cases panic on invalid
// parameters which is not possible with current good API.
Expand All @@ -47,28 +28,91 @@ impl AVAudioFifo {
.unwrap();
}

/// Write data to an AVAudioFifo. If successful, the number of samples
/// actually written will always be nb_samples.
/// Write data to an AVAudioFifo.
///
/// The AVAudioFifo will be reallocated automatically if the available space
/// is less than nb_samples.
///
/// # Safety
/// Function is safe when the `data` points to valid samples.
pub unsafe fn write(&mut self, data: *const *mut u8, nb_samples: i32) -> Result<i32> {
unsafe { ffi::av_audio_fifo_write(self.as_mut_ptr(), data as *mut _, nb_samples) }
.upgrade()
.map_err(RsmpegError::AudioFifoWriteError)
pub unsafe fn write(&mut self, data: *const *mut u8, nb_samples: i32) -> Result<()> {
let ret = unsafe { ffi::av_audio_fifo_write(self.as_mut_ptr(), data as _, nb_samples) }
.upgrade()?;
debug_assert_eq!(ret, nb_samples);
Ok(())
}

/// Peek data from an AVAudioFifo.
///
/// # Safety
/// Function is safe when the `data` points to valid sample buffer.
pub unsafe fn peek(&mut self, data: *const *mut u8, nb_samples: i32) -> Result<i32> {
let ret = unsafe { ffi::av_audio_fifo_peek(self.as_mut_ptr(), data as _, nb_samples) }
.upgrade()?;
Ok(ret)
}

/// Return actually read size if success.
/// Peek data from an AVAudioFifo.
///
/// # Safety
/// Function is safe when the `data` points to valid sample buffer.
pub unsafe fn peek_at(
&mut self,
data: *const *mut u8,
nb_samples: i32,
offset: i32,
) -> Result<i32> {
let ret =
unsafe { ffi::av_audio_fifo_peek_at(self.as_mut_ptr(), data as _, nb_samples, offset) }
.upgrade()?;
Ok(ret)
}

/// Read data from an AVAudioFifo.
///
/// This function returns actually read size if success.
///
/// # Safety
/// Function is safe when the `data` points to valid array such as AVFrame::data.
pub unsafe fn read(&mut self, data: *mut *mut u8, nb_samples: i32) -> Result<i32> {
unsafe { ffi::av_audio_fifo_read(self.as_mut_ptr(), data as _, nb_samples) }
pub unsafe fn read(&mut self, data: *const *mut u8, nb_samples: i32) -> Result<i32> {
let ret = unsafe { ffi::av_audio_fifo_read(self.as_mut_ptr(), data as _, nb_samples) }
.upgrade()?;
Ok(ret)
}

/// Drain data from an AVAudioFifo.
///
/// Removes the data without reading it.
pub fn drain(&mut self, nb_samples: i32) {
// FFI function only error when the nb_samples is negative.
unsafe { ffi::av_audio_fifo_drain(self.as_mut_ptr(), nb_samples) }
.upgrade()
.map_err(RsmpegError::AudioFifoReadError)
.unwrap();
}

/// Reset the AVAudioFifo buffer.
///
/// This empties all data in the buffer.
pub fn reset(&mut self) {
unsafe { ffi::av_audio_fifo_reset(self.as_mut_ptr()) }
}

/// Get the current number of samples in the [`AVAudioFifo`] available for
/// reading.
pub fn size(&self) -> i32 {
unsafe {
// function doesn't modify self, casting safe
ffi::av_audio_fifo_size(self.as_ptr() as *mut _)
}
}

/// Get the current number of samples in the [`AVAudioFifo`] available for
/// writing.
pub fn space(&self) -> i32 {
unsafe {
// function doesn't modify self, casting safe
ffi::av_audio_fifo_space(self.as_ptr() as *mut _)
}
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/avutil/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ impl AVFrame {
Ok(())
}

/// Allocate new buffer(s) for audio or video data.
///
/// The following fields must be set on frame before calling this function:
/// - format (pixel format for video, sample format for audio)
/// - width and height for video
/// - nb_samples and ch_layout for audio
///
/// This function will fill AVFrame.data and AVFrame.buf arrays and, if
/// necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
/// For planar formats, one buffer will be allocated for each plane.
pub fn get_buffer(&mut self, align: i32) -> Result<()> {
unsafe { ffi::av_frame_get_buffer(self.as_mut_ptr(), align) }.upgrade()?;
Ok(())
}

pub fn data_mut(&mut self) -> &mut [*mut u8; 8] {
unsafe { &mut self.deref_mut().data }
}
Expand Down
16 changes: 8 additions & 8 deletions src/avutil/samplefmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ pub fn sample_fmt_is_planar(sample_fmt: AVSampleFormat) -> bool {
// > but for planar audio with more channels that can fit in data,
// > extended_data must be used in order to access all channels.
//
// This is the reason why `AVSamples` has a vector of channels for containing
// This is the reason why `AVSamples` has a vector of channels for holding
// audio data.
wrap! {
AVSamples: Vec<u8>,
audio_data: Vec<*mut u8> = Vec::new(),
AVSamples: Box<[u8]>,
audio_data: Box<[*mut u8]> = Vec::new().into_boxed_slice(),
linesize: i32 = 0,
nb_channels: i32 = 0,
nb_samples: i32 = 0,
Expand Down Expand Up @@ -183,22 +183,22 @@ impl AVSamples {
// Implementation inspired by `av_samples_alloc_array_and_samples` and `av_samples_alloc`.
let (_, buffer_size) =
AVSamples::get_buffer_size(nb_channels, nb_samples, sample_fmt, align)?;
let linear = vec![0u8; buffer_size as usize];
let buffer = vec![0u8; buffer_size as usize].into_boxed_slice();

let nb_planes = if sample_fmt_is_planar(sample_fmt) {
nb_channels
} else {
1
};
let mut audio_data = vec![ptr::null_mut(); nb_planes as usize];
let mut audio_data = vec![ptr::null_mut(); nb_planes as usize].into_boxed_slice();
let mut linesize = 0;
// From the documentation, this function only error on no memory, so
// unwrap.
unsafe {
ffi::av_samples_fill_arrays(
audio_data.as_mut_ptr(),
&mut linesize,
linear.as_ptr(),
buffer.as_ptr(),
nb_channels,
nb_samples,
sample_fmt,
Expand All @@ -209,9 +209,9 @@ impl AVSamples {
.unwrap();

// Leaks a Vec.
let linear = Box::leak(Box::new(linear));
let buffer = Box::leak(Box::new(buffer));

let mut samples = unsafe { AVSamples::from_raw(NonNull::new(linear).unwrap()) };
let mut samples = unsafe { AVSamples::from_raw(NonNull::new(buffer).unwrap()) };
samples.audio_data = audio_data;
samples.linesize = linesize;
samples.nb_channels = nb_channels;
Expand Down
20 changes: 0 additions & 20 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,6 @@ pub enum RsmpegError {
#[error("Failed to initialize bitstream filter context. ({0})")]
BitstreamInitializationError(c_int),

#[error("Read frame to an input format context failed. ({0})")]
ReadFrameError(c_int),
#[error("Write frame to an output format context failed. ({0})")]
WriteFrameError(c_int),
#[error("Interleaved write frame to an output format context failed. ({0})")]
InterleavedWriteFrameError(c_int),

Expand All @@ -104,19 +100,9 @@ pub enum RsmpegError {
#[error("AVIO Open failure. ({0})")]
AVIOOpenError(c_int),

#[error("SwrContext init failed. ({0})")]
SwrContextInitError(c_int),
#[error("SwrContext converting data failed. ({0})")]
SwrConvertError(c_int),

#[error("SwsContext scale failed. ({0})")]
SwsScaleError(c_int),

#[error("AudioFifo write failed. ({0})")]
AudioFifoWriteError(c_int),
#[error("AudioFifo read failed. ({0})")]
AudioFifoReadError(c_int),

#[error("AVFrame buffer double allocating.")]
AVFrameDoubleAllocatingError,
#[error("AVFrame buffer allocating with incorrect parameters. ({0})")]
Expand Down Expand Up @@ -153,19 +139,13 @@ impl RsmpegError {
| Self::BitstreamSendPacketError(err)
| Self::BitstreamReceivePacketError(err)
| Self::BitstreamInitializationError(err)
| Self::ReadFrameError(err)
| Self::WriteFrameError(err)
| Self::InterleavedWriteFrameError(err)
| Self::BufferSrcAddFrameError(err)
| Self::BufferSinkGetFrameError(err)
| Self::DictionaryParseError(err)
| Self::DictionaryGetStringError(err)
| Self::AVIOOpenError(err)
| Self::SwrContextInitError(err)
| Self::SwrConvertError(err)
| Self::SwsScaleError(err)
| Self::AudioFifoWriteError(err)
| Self::AudioFifoReadError(err)
| Self::AVFrameInvalidAllocatingError(err)
| Self::AVImageFillArrayError(err) => Some(*err),

Expand Down
3 changes: 2 additions & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,12 @@ macro_rules! wrap_mut {
/// Wrapping with XXX, XXX -> XXX.
macro_rules! wrap {
(
$(#[$meta:meta])*
$name: ident: $ffi_type: ty
$(,$attach: ident: $attach_type: ty = $attach_default: expr)* $(,)?
) => {
paste::paste! {
wrap_pure!(($name): $ffi_type $(,$attach: $attach_type = $attach_default)*);
wrap_pure!($(#[$meta])* ($name): $ffi_type $(,$attach: $attach_type = $attach_default)*);
}
};
}
Expand Down
Loading

0 comments on commit d816f71

Please sign in to comment.