use std::{path::Path, process};
use avio::AsyncAudioDecoder;
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1);
let mut input = None::<String>;
while let Some(flag) = args.next() {
match flag.as_str() {
"--input" | "-i" => input = Some(args.next().unwrap_or_default()),
other => {
eprintln!("Unknown flag: {other}");
process::exit(1);
}
}
}
let input = input.unwrap_or_else(|| {
eprintln!("Usage: async_decode_audio --input <file>");
process::exit(1);
});
let in_name = Path::new(&input)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(&input);
println!("Input: {in_name}");
println!();
println!("=== Pattern 1: frame-by-frame with decode_frame() ===");
let mut decoder = match AsyncAudioDecoder::open(input.clone()).await {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping (open failed): {e}");
return Ok(());
}
};
let mut frame_count: u64 = 0;
let mut total_samples: u64 = 0;
loop {
match decoder.decode_frame().await {
Ok(Some(frame)) => {
if frame_count == 0 {
println!(
"First frame: samples={} channels={} sample_rate={} format={}",
frame.samples(),
frame.channels(),
frame.sample_rate(),
frame.format(),
);
}
total_samples += frame.samples() as u64;
frame_count += 1;
}
Ok(None) => break,
Err(e) => {
eprintln!("Decode error: {e}");
break;
}
}
}
println!("Decoded {frame_count} frames ({total_samples} samples)");
println!();
println!("=== Pattern 2: stream API with into_stream() — sample counting ===");
let decoder = match AsyncAudioDecoder::open(input.clone()).await {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping (open failed): {e}");
return Ok(());
}
};
let stream_samples: u64 = decoder
.into_stream()
.filter_map(|result| async move { result.ok() })
.map(|frame| frame.samples() as u64)
.fold(0u64, |acc, n| async move { acc + n })
.await;
println!("Total samples via stream: {stream_samples}");
println!();
println!("=== Pattern 3: stream spawned on a background task ===");
let decoder = match AsyncAudioDecoder::open(input.clone()).await {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping (open failed): {e}");
return Ok(());
}
};
let handle = tokio::spawn(async move {
let mut total: u64 = 0;
let stream = decoder.into_stream();
tokio::pin!(stream);
while let Some(result) = stream.next().await {
match result {
Ok(frame) => total += frame.samples() as u64,
Err(e) => {
eprintln!("Background decode error: {e}");
break;
}
}
}
total
});
let background_samples = handle.await?;
println!("Background task counted {background_samples} samples");
Ok(())
}