Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise transcode_aac example and various updates #147

Merged
merged 11 commits into from
Jan 23, 2024
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
Loading