use std::collections::VecDeque;
use alpha2_media::{AudioBlock, EncodeError};
use rsmpeg::avcodec::{AVCodecContext, AVCodecRef};
use rsmpeg::avutil::{AVChannelLayout, AVRational, ra};
use rsmpeg::ffi;
use crate::audio_timeline::SourceTimeline;
use crate::capabilities::NativeAudioCodec;
use crate::convert::encode_adapter_error;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct AudioStreamConfig {
pub codec: NativeAudioCodec,
pub sample_rate: u32,
pub channels: u16,
pub bit_rate: u32,
}
pub(crate) struct OpenedAudioEncoder {
pub(crate) encoder: AVCodecContext,
pub(crate) encoder_time_base: AVRational,
pub(crate) input: EncoderAudioInput,
pub(crate) encoder_rate: u32,
pub(crate) sample_format: ffi::AVSampleFormat,
}
pub(crate) fn open_audio_encoder(
config: AudioStreamConfig,
global_header: bool,
) -> Result<OpenedAudioEncoder, EncodeError> {
let AudioStreamConfig {
codec,
sample_rate,
channels,
bit_rate,
} = config;
let encoder_codec = crate::capabilities::audio_encoder(codec).ok_or_else(|| {
EncodeError::Adapter(format!(
"this FFmpeg runtime has no {} encoder",
codec.as_str()
))
})?;
let sample_format = encoder_sample_format(&encoder_codec).ok_or_else(|| {
EncodeError::Adapter(format!(
"the {} encoder takes neither float nor 16-bit samples",
codec.as_str()
))
})?;
let encoder_rate = encoder_sample_rate(&encoder_codec, sample_rate);
let rate = i32::try_from(encoder_rate)
.map_err(|_| EncodeError::Adapter("audio sample rate is out of range".into()))?;
let encoder_time_base = ra(1, rate);
let mut encoder = AVCodecContext::new(&encoder_codec);
encoder.set_sample_rate(rate);
encoder.set_sample_fmt(sample_format);
encoder.set_ch_layout(AVChannelLayout::from_nb_channels(i32::from(channels)).into_inner());
encoder.set_time_base(encoder_time_base);
encoder.set_bit_rate(i64::from(bit_rate));
if global_header {
encoder.set_flags(
encoder.flags | i32::try_from(ffi::AV_CODEC_FLAG_GLOBAL_HEADER).unwrap_or(0),
);
}
encoder.open(None).map_err(encode_adapter_error)?;
let frame_size = usize::try_from(encoder.frame_size)
.ok()
.filter(|size| *size > 0)
.ok_or_else(|| {
EncodeError::Adapter(format!(
"the {} encoder has no fixed frame size",
codec.as_str()
))
})?;
Ok(OpenedAudioEncoder {
encoder,
encoder_time_base,
input: EncoderAudioInput::new(sample_rate, encoder_rate, usize::from(channels), frame_size),
encoder_rate,
sample_format,
})
}
pub(crate) fn encoder_sample_format(codec: &AVCodecRef<'_>) -> Option<ffi::AVSampleFormat> {
let Some(formats) = codec.sample_fmts() else {
return Some(ffi::AV_SAMPLE_FMT_FLTP);
};
[
ffi::AV_SAMPLE_FMT_FLTP,
ffi::AV_SAMPLE_FMT_FLT,
ffi::AV_SAMPLE_FMT_S16,
]
.into_iter()
.find(|format| formats.contains(format))
}
pub(crate) fn encoder_sample_rate(codec: &AVCodecRef<'_>, project_rate: u32) -> u32 {
let Some(rates) = codec.supported_samplerates() else {
return project_rate;
};
let rates = rates
.iter()
.filter_map(|rate| u32::try_from(*rate).ok())
.filter(|rate| *rate > 0)
.collect::<Vec<_>>();
if rates.contains(&project_rate) {
return project_rate;
}
rates
.iter()
.copied()
.filter(|rate| *rate > project_rate)
.min()
.or_else(|| rates.iter().copied().max())
.unwrap_or(project_rate)
}
pub(crate) struct EncoderAudioInput {
project_rate: u32,
channels: usize,
frame_size: usize,
resampler: Option<SourceTimeline>,
pending: Vec<VecDeque<f32>>,
received: u64,
}
impl EncoderAudioInput {
pub(crate) fn new(
project_rate: u32,
encoder_rate: u32,
channels: usize,
frame_size: usize,
) -> Self {
Self {
project_rate,
channels,
frame_size,
resampler: (encoder_rate != project_rate)
.then(|| SourceTimeline::new(encoder_rate, channels, Some(0))),
pending: (0..channels).map(|_| VecDeque::new()).collect(),
received: 0,
}
}
pub(crate) fn push(&mut self, block: &AudioBlock) -> Result<Vec<Vec<Vec<f32>>>, EncodeError> {
if block.sample_rate != self.project_rate
|| block.channels.len() != self.channels
|| block.channels.is_empty()
{
return Err(EncodeError::Adapter("audio block format changed".into()));
}
let frames = block.channels[0].len();
if block.channels.iter().any(|channel| channel.len() != frames) {
return Err(EncodeError::Adapter("audio channel lengths differ".into()));
}
if u64::try_from(block.start_sample).ok() != Some(self.received) {
return Err(EncodeError::InvalidTimestamp);
}
match self.resampler.as_mut() {
Some(resampler) => {
resampler
.place(
self.project_rate,
block.start_sample,
block.channels.clone(),
)
.map_err(|error| EncodeError::Adapter(error.to_string()))?;
resampler.produce(&mut self.pending, usize::MAX);
}
None => {
for (pending, channel) in self.pending.iter_mut().zip(&block.channels) {
pending.extend(channel.iter().copied());
}
}
}
self.received = self
.received
.saturating_add(u64::try_from(frames).unwrap_or(u64::MAX));
let mut ready = Vec::new();
while self.pending[0].len() >= self.frame_size {
ready.push(self.take(self.frame_size));
}
Ok(ready)
}
pub(crate) fn finish(&mut self) -> Vec<Vec<Vec<f32>>> {
if let Some(resampler) = self.resampler.as_mut() {
resampler.finish();
resampler.produce(&mut self.pending, usize::MAX);
}
let mut frames = Vec::new();
while !self.pending[0].is_empty() {
frames.push(self.take(self.frame_size.min(self.pending[0].len())));
}
frames
}
pub(crate) fn received_ticks(&self) -> i64 {
i64::try_from(self.received)
.unwrap_or(i64::MAX)
.saturating_mul(alpha2_time::TICKS_PER_SECOND)
.div_euclid(i64::from(self.project_rate))
}
fn take(&mut self, samples: usize) -> Vec<Vec<f32>> {
self.pending
.iter_mut()
.map(|pending| pending.drain(..samples).collect())
.collect()
}
}