#![warn(clippy::default_trait_access)]
#![warn(clippy::elidable_lifetime_names)]
#![warn(clippy::uninlined_format_args)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![warn(rust_2021_compatibility)]
#![warn(rust_2024_compatibility)]
use std::ffi::CStr;
use std::path::Path;
pub use error::{Error, FFmpegError};
mod avio;
mod encoder;
mod error;
mod loader;
mod sys;
mod util;
#[derive(Debug, Clone)]
pub enum SampleData {
F64(Vec<f64>),
F32(Vec<f32>),
I64(Vec<i64>),
I32(Vec<i32>),
I16(Vec<i16>),
U8(Vec<u8>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChannelLayout {
Planar,
Interleaved,
}
#[derive(Debug, Clone)]
pub struct AudioBuffer {
pub samples: SampleData,
pub channels: u32,
pub frames: u64,
pub sample_rate: u32,
pub layout: ChannelLayout,
}
#[derive(Debug, Clone)]
pub struct AudioInfo {
pub sample_rate: u32,
pub channels: u32,
pub frames: Option<u64>,
pub bits_per_sample: Option<u32>,
pub codec: Option<String>,
}
#[derive(Debug, Clone)]
pub struct LoadOptions {
pub sample_rate: Option<u32>,
pub mono: bool,
pub frame_offset: u64,
pub num_frames: Option<u64>,
pub normalize: bool,
pub channels_first: bool,
}
impl Default for LoadOptions {
fn default() -> Self {
Self {
sample_rate: None,
mono: false,
frame_offset: 0,
num_frames: None,
normalize: true,
channels_first: true,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum WavSampleFormat {
U8,
I16,
I24,
I32,
F32,
F64,
}
#[derive(Debug, Clone, Copy)]
pub enum AiffSampleFormat {
I8,
I16,
I24,
I32,
F32,
F64,
}
#[derive(Debug, Clone, Copy)]
pub enum FlacBitsPerSample {
Bits16,
Bits24,
}
#[derive(Debug, Clone, Copy)]
pub enum AlacBitsPerSample {
Bits16,
Bits24,
}
#[derive(Debug, Clone, Copy, Default)]
pub enum OpusApplication {
Voip,
#[default]
Audio,
LowDelay,
}
#[derive(Debug, Clone, Copy, Default)]
pub enum OpusFrameDuration {
D2_5ms,
D5ms,
D10ms,
#[default]
D20ms,
D40ms,
D60ms,
}
#[derive(Debug, Clone, Copy, Default)]
pub enum OpusVbrMode {
Off,
#[default]
On,
Constrained,
}
#[derive(Debug, Clone, Copy)]
pub enum Codec {
Wav {
sample_format: WavSampleFormat,
},
Aiff {
sample_format: AiffSampleFormat,
},
Flac {
compression_level: Option<u32>,
bits_per_sample: FlacBitsPerSample,
},
Alac {
bits_per_sample: AlacBitsPerSample,
},
Aac {
bit_rate: Option<u32>,
},
Mp3 {
bit_rate: Option<u32>,
compression_level: Option<u32>,
},
Opus {
bit_rate: Option<u32>,
application: Option<OpusApplication>,
frame_duration: Option<OpusFrameDuration>,
vbr: Option<OpusVbrMode>,
},
Vorbis {
quality: Option<f32>,
bit_rate: Option<u32>,
},
}
#[derive(Debug, Clone)]
pub struct SaveOptions {
pub codec: Codec,
pub sample_rate: Option<u32>,
pub mono: bool,
}
impl Default for SaveOptions {
fn default() -> Self {
Self {
codec: Codec::Wav {
sample_format: WavSampleFormat::I16,
},
sample_rate: None,
mono: false,
}
}
}
impl WavSampleFormat {
pub(crate) fn encoder_name(self) -> &'static CStr {
match self {
Self::U8 => c"pcm_u8",
Self::I16 => c"pcm_s16le",
Self::I24 => c"pcm_s24le",
Self::I32 => c"pcm_s32le",
Self::F32 => c"pcm_f32le",
Self::F64 => c"pcm_f64le",
}
}
pub(crate) fn sample_fmt(self) -> i32 {
match self {
Self::U8 => sys::AV_SAMPLE_FMT_U8,
Self::I16 => sys::AV_SAMPLE_FMT_S16,
Self::I24 | Self::I32 => sys::AV_SAMPLE_FMT_S32,
Self::F32 => sys::AV_SAMPLE_FMT_FLT,
Self::F64 => sys::AV_SAMPLE_FMT_DBL,
}
}
}
impl AiffSampleFormat {
pub(crate) fn encoder_name(self) -> &'static CStr {
match self {
Self::I8 => c"pcm_s8",
Self::I16 => c"pcm_s16be",
Self::I24 => c"pcm_s24be",
Self::I32 => c"pcm_s32be",
Self::F32 => c"pcm_f32be",
Self::F64 => c"pcm_f64be",
}
}
pub(crate) fn sample_fmt(self) -> i32 {
match self {
Self::I8 => sys::AV_SAMPLE_FMT_U8,
Self::I16 => sys::AV_SAMPLE_FMT_S16,
Self::I24 | Self::I32 => sys::AV_SAMPLE_FMT_S32,
Self::F32 => sys::AV_SAMPLE_FMT_FLT,
Self::F64 => sys::AV_SAMPLE_FMT_DBL,
}
}
}
impl FlacBitsPerSample {
pub(crate) fn sample_fmt(self) -> i32 {
match self {
Self::Bits16 => sys::AV_SAMPLE_FMT_S16,
Self::Bits24 => sys::AV_SAMPLE_FMT_S32,
}
}
pub(crate) fn bits_per_raw_sample(self) -> i32 {
match self {
Self::Bits16 => 16,
Self::Bits24 => 24,
}
}
}
impl AlacBitsPerSample {
pub(crate) fn sample_fmt(self) -> i32 {
match self {
Self::Bits16 => sys::AV_SAMPLE_FMT_S16P,
Self::Bits24 => sys::AV_SAMPLE_FMT_S32P,
}
}
pub(crate) fn bits_per_raw_sample(self) -> i32 {
match self {
Self::Bits16 => 16,
Self::Bits24 => 24,
}
}
}
impl OpusApplication {
pub(crate) fn av_opt_value(self) -> &'static CStr {
match self {
Self::Voip => c"voip",
Self::Audio => c"audio",
Self::LowDelay => c"lowdelay",
}
}
}
impl OpusFrameDuration {
pub(crate) fn millis(self) -> f64 {
match self {
Self::D2_5ms => 2.5,
Self::D5ms => 5.0,
Self::D10ms => 10.0,
Self::D20ms => 20.0,
Self::D40ms => 40.0,
Self::D60ms => 60.0,
}
}
}
impl OpusVbrMode {
pub(crate) fn av_opt_value(self) -> &'static CStr {
match self {
Self::Off => c"off",
Self::On => c"on",
Self::Constrained => c"constrained",
}
}
}
pub fn info<P: AsRef<Path>>(path: P) -> Result<AudioInfo, Error> {
loader::info(path.as_ref())
}
pub fn load<P: AsRef<Path>>(path: P, opts: &LoadOptions) -> Result<AudioBuffer, Error> {
if let Some(0) = opts.sample_rate {
return Err(Error::InvalidArg("sample_rate must be > 0 when set"));
}
if let Some(0) = opts.num_frames {
return Err(Error::InvalidArg("num_frames must be > 0 when set"));
}
loader::load(path.as_ref(), opts)
}
pub fn save<P: AsRef<Path>>(path: P, buf: &AudioBuffer, opts: &SaveOptions) -> Result<(), Error> {
if let Some(0) = opts.sample_rate {
return Err(Error::InvalidArg("sample_rate must be > 0 when set"));
}
encoder::save(path.as_ref(), buf, opts)
}
pub fn info_bytes(data: &[u8]) -> Result<AudioInfo, Error> {
loader::info_bytes(data)
}
pub fn load_bytes(data: &[u8], opts: &LoadOptions) -> Result<AudioBuffer, Error> {
if let Some(0) = opts.sample_rate {
return Err(Error::InvalidArg("sample_rate must be > 0 when set"));
}
if let Some(0) = opts.num_frames {
return Err(Error::InvalidArg("num_frames must be > 0 when set"));
}
loader::load_bytes(data, opts)
}
pub fn save_bytes(buf: &AudioBuffer, opts: &SaveOptions) -> Result<Vec<u8>, Error> {
if let Some(0) = opts.sample_rate {
return Err(Error::InvalidArg("sample_rate must be > 0 when set"));
}
encoder::save_bytes(buf, opts)
}