use std::time::Duration;
use crate::channel::ChannelLayout;
use crate::codec::AudioCodec;
use crate::sample::SampleFormat;
#[derive(Debug, Clone)]
pub struct AudioStreamInfo {
index: u32,
codec: AudioCodec,
codec_name: String,
sample_rate: u32,
channels: u32,
channel_layout: ChannelLayout,
sample_format: SampleFormat,
duration: Option<Duration>,
bitrate: Option<u64>,
language: Option<String>,
}
impl AudioStreamInfo {
#[must_use]
pub fn builder() -> AudioStreamInfoBuilder {
AudioStreamInfoBuilder::default()
}
#[must_use]
#[inline]
pub const fn index(&self) -> u32 {
self.index
}
#[must_use]
#[inline]
pub const fn codec(&self) -> AudioCodec {
self.codec
}
#[must_use]
#[inline]
pub fn codec_name(&self) -> &str {
&self.codec_name
}
#[must_use]
#[inline]
pub const fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[must_use]
#[inline]
pub const fn channels(&self) -> u32 {
self.channels
}
#[must_use]
#[inline]
pub const fn channel_layout(&self) -> ChannelLayout {
self.channel_layout
}
#[must_use]
#[inline]
pub const fn sample_format(&self) -> SampleFormat {
self.sample_format
}
#[must_use]
#[inline]
pub const fn duration(&self) -> Option<Duration> {
self.duration
}
#[must_use]
#[inline]
pub const fn bitrate(&self) -> Option<u64> {
self.bitrate
}
#[must_use]
#[inline]
pub fn language(&self) -> Option<&str> {
self.language.as_deref()
}
#[must_use]
#[inline]
pub const fn is_mono(&self) -> bool {
self.channels == 1
}
#[must_use]
#[inline]
pub const fn is_stereo(&self) -> bool {
self.channels == 2
}
#[must_use]
#[inline]
pub const fn is_surround(&self) -> bool {
self.channels > 2
}
}
impl Default for AudioStreamInfo {
fn default() -> Self {
Self {
index: 0,
codec: AudioCodec::default(),
codec_name: String::new(),
sample_rate: 48000,
channels: 2,
channel_layout: ChannelLayout::default(),
sample_format: SampleFormat::default(),
duration: None,
bitrate: None,
language: None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct AudioStreamInfoBuilder {
index: u32,
codec: AudioCodec,
codec_name: String,
sample_rate: u32,
channels: u32,
channel_layout: Option<ChannelLayout>,
sample_format: SampleFormat,
duration: Option<Duration>,
bitrate: Option<u64>,
language: Option<String>,
}
impl AudioStreamInfoBuilder {
#[must_use]
pub fn index(mut self, index: u32) -> Self {
self.index = index;
self
}
#[must_use]
pub fn codec(mut self, codec: AudioCodec) -> Self {
self.codec = codec;
self
}
#[must_use]
pub fn codec_name(mut self, name: impl Into<String>) -> Self {
self.codec_name = name.into();
self
}
#[must_use]
pub fn sample_rate(mut self, rate: u32) -> Self {
self.sample_rate = rate;
self
}
#[must_use]
pub fn channels(mut self, channels: u32) -> Self {
self.channels = channels;
self
}
#[must_use]
pub fn channel_layout(mut self, layout: ChannelLayout) -> Self {
self.channel_layout = Some(layout);
self
}
#[must_use]
pub fn sample_format(mut self, format: SampleFormat) -> Self {
self.sample_format = format;
self
}
#[must_use]
pub fn duration(mut self, duration: Duration) -> Self {
self.duration = Some(duration);
self
}
#[must_use]
pub fn bitrate(mut self, bitrate: u64) -> Self {
self.bitrate = Some(bitrate);
self
}
#[must_use]
pub fn language(mut self, lang: impl Into<String>) -> Self {
self.language = Some(lang.into());
self
}
#[must_use]
pub fn build(self) -> AudioStreamInfo {
let channel_layout = self.channel_layout.unwrap_or_else(|| {
log::warn!(
"channel_layout not set, deriving from channel count \
channels={} fallback=from_channels",
self.channels
);
ChannelLayout::from_channels(self.channels)
});
AudioStreamInfo {
index: self.index,
codec: self.codec,
codec_name: self.codec_name,
sample_rate: self.sample_rate,
channels: self.channels,
channel_layout,
sample_format: self.sample_format,
duration: self.duration,
bitrate: self.bitrate,
language: self.language,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_basic() {
let info = AudioStreamInfo::builder()
.index(1)
.codec(AudioCodec::Aac)
.codec_name("aac")
.sample_rate(48000)
.channels(2)
.sample_format(SampleFormat::F32)
.build();
assert_eq!(info.index(), 1);
assert_eq!(info.codec(), AudioCodec::Aac);
assert_eq!(info.codec_name(), "aac");
assert_eq!(info.sample_rate(), 48000);
assert_eq!(info.channels(), 2);
assert_eq!(info.sample_format(), SampleFormat::F32);
assert_eq!(info.channel_layout(), ChannelLayout::Stereo);
}
#[test]
fn test_builder_full() {
let info = AudioStreamInfo::builder()
.index(2)
.codec(AudioCodec::Flac)
.codec_name("flac")
.sample_rate(96000)
.channels(6)
.channel_layout(ChannelLayout::Surround5_1)
.sample_format(SampleFormat::I32)
.duration(Duration::from_secs(300))
.bitrate(1_411_200)
.language("jpn")
.build();
assert_eq!(info.codec(), AudioCodec::Flac);
assert_eq!(info.sample_rate(), 96000);
assert_eq!(info.channels(), 6);
assert_eq!(info.channel_layout(), ChannelLayout::Surround5_1);
assert_eq!(info.duration(), Some(Duration::from_secs(300)));
assert_eq!(info.bitrate(), Some(1_411_200));
assert_eq!(info.language(), Some("jpn"));
}
#[test]
fn test_default() {
let info = AudioStreamInfo::default();
assert_eq!(info.index(), 0);
assert_eq!(info.codec(), AudioCodec::default());
assert_eq!(info.sample_rate(), 48000);
assert_eq!(info.channels(), 2);
assert!(info.duration().is_none());
}
#[test]
fn test_auto_channel_layout() {
let mono = AudioStreamInfo::builder().channels(1).build();
assert_eq!(mono.channel_layout(), ChannelLayout::Mono);
let stereo = AudioStreamInfo::builder().channels(2).build();
assert_eq!(stereo.channel_layout(), ChannelLayout::Stereo);
let surround = AudioStreamInfo::builder().channels(6).build();
assert_eq!(surround.channel_layout(), ChannelLayout::Surround5_1);
let custom = AudioStreamInfo::builder()
.channels(6)
.channel_layout(ChannelLayout::Other(6))
.build();
assert_eq!(custom.channel_layout(), ChannelLayout::Other(6));
}
#[test]
fn test_channel_checks() {
let mono = AudioStreamInfo::builder().channels(1).build();
assert!(mono.is_mono());
assert!(!mono.is_stereo());
assert!(!mono.is_surround());
let stereo = AudioStreamInfo::builder().channels(2).build();
assert!(!stereo.is_mono());
assert!(stereo.is_stereo());
assert!(!stereo.is_surround());
let surround = AudioStreamInfo::builder().channels(6).build();
assert!(!surround.is_mono());
assert!(!surround.is_stereo());
assert!(surround.is_surround());
}
#[test]
fn test_debug() {
let info = AudioStreamInfo::builder()
.index(1)
.codec(AudioCodec::Aac)
.sample_rate(48000)
.channels(2)
.build();
let debug = format!("{info:?}");
assert!(debug.contains("AudioStreamInfo"));
assert!(debug.contains("48000"));
}
#[test]
fn test_clone() {
let info = AudioStreamInfo::builder()
.index(1)
.codec(AudioCodec::Aac)
.codec_name("aac")
.sample_rate(48000)
.channels(2)
.language("eng")
.build();
let cloned = info.clone();
assert_eq!(info.sample_rate(), cloned.sample_rate());
assert_eq!(info.channels(), cloned.channels());
assert_eq!(info.language(), cloned.language());
assert_eq!(info.codec_name(), cloned.codec_name());
}
}