ff_format/stream/
audio.rs1use std::time::Duration;
4
5use crate::channel::ChannelLayout;
6use crate::codec::AudioCodec;
7use crate::sample::SampleFormat;
8
9#[derive(Debug, Clone)]
32pub struct AudioStreamInfo {
33 index: u32,
35 codec: AudioCodec,
37 codec_name: String,
39 sample_rate: u32,
41 channels: u32,
43 channel_layout: ChannelLayout,
45 sample_format: SampleFormat,
47 duration: Option<Duration>,
49 bitrate: Option<u64>,
51 language: Option<String>,
53}
54
55impl AudioStreamInfo {
56 #[must_use]
73 pub fn builder() -> AudioStreamInfoBuilder {
74 AudioStreamInfoBuilder::default()
75 }
76
77 #[must_use]
79 #[inline]
80 pub const fn index(&self) -> u32 {
81 self.index
82 }
83
84 #[must_use]
86 #[inline]
87 pub const fn codec(&self) -> AudioCodec {
88 self.codec
89 }
90
91 #[must_use]
93 #[inline]
94 pub fn codec_name(&self) -> &str {
95 &self.codec_name
96 }
97
98 #[must_use]
100 #[inline]
101 pub const fn sample_rate(&self) -> u32 {
102 self.sample_rate
103 }
104
105 #[must_use]
112 #[inline]
113 pub const fn channels(&self) -> u32 {
114 self.channels
115 }
116
117 #[must_use]
119 #[inline]
120 pub const fn channel_layout(&self) -> ChannelLayout {
121 self.channel_layout
122 }
123
124 #[must_use]
126 #[inline]
127 pub const fn sample_format(&self) -> SampleFormat {
128 self.sample_format
129 }
130
131 #[must_use]
133 #[inline]
134 pub const fn duration(&self) -> Option<Duration> {
135 self.duration
136 }
137
138 #[must_use]
140 #[inline]
141 pub const fn bitrate(&self) -> Option<u64> {
142 self.bitrate
143 }
144
145 #[must_use]
147 #[inline]
148 pub fn language(&self) -> Option<&str> {
149 self.language.as_deref()
150 }
151
152 #[must_use]
154 #[inline]
155 pub const fn is_mono(&self) -> bool {
156 self.channels == 1
157 }
158
159 #[must_use]
161 #[inline]
162 pub const fn is_stereo(&self) -> bool {
163 self.channels == 2
164 }
165
166 #[must_use]
168 #[inline]
169 pub const fn is_surround(&self) -> bool {
170 self.channels > 2
171 }
172}
173
174impl Default for AudioStreamInfo {
175 fn default() -> Self {
176 Self {
177 index: 0,
178 codec: AudioCodec::default(),
179 codec_name: String::new(),
180 sample_rate: 48000,
181 channels: 2,
182 channel_layout: ChannelLayout::default(),
183 sample_format: SampleFormat::default(),
184 duration: None,
185 bitrate: None,
186 language: None,
187 }
188 }
189}
190
191#[derive(Debug, Clone, Default)]
193pub struct AudioStreamInfoBuilder {
194 index: u32,
195 codec: AudioCodec,
196 codec_name: String,
197 sample_rate: u32,
198 channels: u32,
199 channel_layout: Option<ChannelLayout>,
200 sample_format: SampleFormat,
201 duration: Option<Duration>,
202 bitrate: Option<u64>,
203 language: Option<String>,
204}
205
206impl AudioStreamInfoBuilder {
207 #[must_use]
209 pub fn index(mut self, index: u32) -> Self {
210 self.index = index;
211 self
212 }
213
214 #[must_use]
216 pub fn codec(mut self, codec: AudioCodec) -> Self {
217 self.codec = codec;
218 self
219 }
220
221 #[must_use]
223 pub fn codec_name(mut self, name: impl Into<String>) -> Self {
224 self.codec_name = name.into();
225 self
226 }
227
228 #[must_use]
230 pub fn sample_rate(mut self, rate: u32) -> Self {
231 self.sample_rate = rate;
232 self
233 }
234
235 #[must_use]
239 pub fn channels(mut self, channels: u32) -> Self {
240 self.channels = channels;
241 self
242 }
243
244 #[must_use]
246 pub fn channel_layout(mut self, layout: ChannelLayout) -> Self {
247 self.channel_layout = Some(layout);
248 self
249 }
250
251 #[must_use]
253 pub fn sample_format(mut self, format: SampleFormat) -> Self {
254 self.sample_format = format;
255 self
256 }
257
258 #[must_use]
260 pub fn duration(mut self, duration: Duration) -> Self {
261 self.duration = Some(duration);
262 self
263 }
264
265 #[must_use]
267 pub fn bitrate(mut self, bitrate: u64) -> Self {
268 self.bitrate = Some(bitrate);
269 self
270 }
271
272 #[must_use]
274 pub fn language(mut self, lang: impl Into<String>) -> Self {
275 self.language = Some(lang.into());
276 self
277 }
278
279 #[must_use]
281 pub fn build(self) -> AudioStreamInfo {
282 let channel_layout = self.channel_layout.unwrap_or_else(|| {
283 log::warn!(
284 "channel_layout not set, deriving from channel count \
285 channels={} fallback=from_channels",
286 self.channels
287 );
288 ChannelLayout::from_channels(self.channels)
289 });
290
291 AudioStreamInfo {
292 index: self.index,
293 codec: self.codec,
294 codec_name: self.codec_name,
295 sample_rate: self.sample_rate,
296 channels: self.channels,
297 channel_layout,
298 sample_format: self.sample_format,
299 duration: self.duration,
300 bitrate: self.bitrate,
301 language: self.language,
302 }
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn test_builder_basic() {
312 let info = AudioStreamInfo::builder()
313 .index(1)
314 .codec(AudioCodec::Aac)
315 .codec_name("aac")
316 .sample_rate(48000)
317 .channels(2)
318 .sample_format(SampleFormat::F32)
319 .build();
320
321 assert_eq!(info.index(), 1);
322 assert_eq!(info.codec(), AudioCodec::Aac);
323 assert_eq!(info.codec_name(), "aac");
324 assert_eq!(info.sample_rate(), 48000);
325 assert_eq!(info.channels(), 2);
326 assert_eq!(info.sample_format(), SampleFormat::F32);
327 assert_eq!(info.channel_layout(), ChannelLayout::Stereo);
328 }
329
330 #[test]
331 fn test_builder_full() {
332 let info = AudioStreamInfo::builder()
333 .index(2)
334 .codec(AudioCodec::Flac)
335 .codec_name("flac")
336 .sample_rate(96000)
337 .channels(6)
338 .channel_layout(ChannelLayout::Surround5_1)
339 .sample_format(SampleFormat::I32)
340 .duration(Duration::from_secs(300))
341 .bitrate(1_411_200)
342 .language("jpn")
343 .build();
344
345 assert_eq!(info.codec(), AudioCodec::Flac);
346 assert_eq!(info.sample_rate(), 96000);
347 assert_eq!(info.channels(), 6);
348 assert_eq!(info.channel_layout(), ChannelLayout::Surround5_1);
349 assert_eq!(info.duration(), Some(Duration::from_secs(300)));
350 assert_eq!(info.bitrate(), Some(1_411_200));
351 assert_eq!(info.language(), Some("jpn"));
352 }
353
354 #[test]
355 fn test_default() {
356 let info = AudioStreamInfo::default();
357 assert_eq!(info.index(), 0);
358 assert_eq!(info.codec(), AudioCodec::default());
359 assert_eq!(info.sample_rate(), 48000);
360 assert_eq!(info.channels(), 2);
361 assert!(info.duration().is_none());
362 }
363
364 #[test]
365 fn test_auto_channel_layout() {
366 let mono = AudioStreamInfo::builder().channels(1).build();
368 assert_eq!(mono.channel_layout(), ChannelLayout::Mono);
369
370 let stereo = AudioStreamInfo::builder().channels(2).build();
371 assert_eq!(stereo.channel_layout(), ChannelLayout::Stereo);
372
373 let surround = AudioStreamInfo::builder().channels(6).build();
374 assert_eq!(surround.channel_layout(), ChannelLayout::Surround5_1);
375
376 let custom = AudioStreamInfo::builder()
378 .channels(6)
379 .channel_layout(ChannelLayout::Other(6))
380 .build();
381 assert_eq!(custom.channel_layout(), ChannelLayout::Other(6));
382 }
383
384 #[test]
385 fn test_channel_checks() {
386 let mono = AudioStreamInfo::builder().channels(1).build();
387 assert!(mono.is_mono());
388 assert!(!mono.is_stereo());
389 assert!(!mono.is_surround());
390
391 let stereo = AudioStreamInfo::builder().channels(2).build();
392 assert!(!stereo.is_mono());
393 assert!(stereo.is_stereo());
394 assert!(!stereo.is_surround());
395
396 let surround = AudioStreamInfo::builder().channels(6).build();
397 assert!(!surround.is_mono());
398 assert!(!surround.is_stereo());
399 assert!(surround.is_surround());
400 }
401
402 #[test]
403 fn test_debug() {
404 let info = AudioStreamInfo::builder()
405 .index(1)
406 .codec(AudioCodec::Aac)
407 .sample_rate(48000)
408 .channels(2)
409 .build();
410 let debug = format!("{info:?}");
411 assert!(debug.contains("AudioStreamInfo"));
412 assert!(debug.contains("48000"));
413 }
414
415 #[test]
416 fn test_clone() {
417 let info = AudioStreamInfo::builder()
418 .index(1)
419 .codec(AudioCodec::Aac)
420 .codec_name("aac")
421 .sample_rate(48000)
422 .channels(2)
423 .language("eng")
424 .build();
425 let cloned = info.clone();
426 assert_eq!(info.sample_rate(), cloned.sample_rate());
427 assert_eq!(info.channels(), cloned.channels());
428 assert_eq!(info.language(), cloned.language());
429 assert_eq!(info.codec_name(), cloned.codec_name());
430 }
431}