Skip to main content

audio_extraction/
audio_extraction.rs

1//! Extract the audio track from a video file to a standalone audio file.
2//!
3//! Uses [`AudioExtractor`] to copy the audio stream out of a container without
4//! re-encoding.  The output format is determined by the output file extension
5//! (e.g., `.m4a`, `.aac`, `.mp3`, `.opus`).
6//!
7//! # Usage
8//!
9//! ```bash
10//! cargo run --example audio_extraction -- \
11//!     --input  video.mp4     \
12//!     --output audio.m4a
13//!
14//! # Extract a specific audio stream (0-based index):
15//! cargo run --example audio_extraction -- \
16//!     --input  video.mp4     \
17//!     --output audio.m4a     \
18//!     --stream 1
19//! ```
20
21use std::process;
22
23use ff_remux::AudioExtractor;
24
25fn main() {
26    let mut args = std::env::args().skip(1);
27    let mut input = None::<String>;
28    let mut output = None::<String>;
29    let mut stream_index = None::<usize>;
30
31    while let Some(flag) = args.next() {
32        match flag.as_str() {
33            "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
34            "--output" | "-o" => output = Some(args.next().unwrap_or_default()),
35            "--stream" | "-s" => {
36                let raw = args.next().unwrap_or_default();
37                stream_index = Some(raw.parse().unwrap_or_else(|_| {
38                    eprintln!("Invalid stream index: {raw}");
39                    process::exit(1);
40                }));
41            }
42            other => {
43                eprintln!("Unknown flag: {other}");
44                process::exit(1);
45            }
46        }
47    }
48
49    let input = input.unwrap_or_else(|| {
50        eprintln!("Usage: audio_extraction --input <file> --output <audio> [--stream <index>]");
51        process::exit(1);
52    });
53    let output = output.unwrap_or_else(|| {
54        eprintln!("--output is required");
55        process::exit(1);
56    });
57
58    println!("Input:  {input}");
59    println!(
60        "Stream: {}",
61        stream_index.map_or_else(
62            || "first audio stream (default)".to_string(),
63            |i| i.to_string()
64        )
65    );
66    println!("Output: {output}");
67    println!();
68    println!("Extracting audio track (stream-copy, no re-encode)…");
69
70    let mut extractor = AudioExtractor::new(&input, &output);
71    if let Some(idx) = stream_index {
72        extractor = extractor.stream_index(idx);
73    }
74
75    extractor.run().unwrap_or_else(|e| {
76        eprintln!("Error: {e}");
77        process::exit(1);
78    });
79
80    let size = match std::fs::metadata(&output) {
81        Ok(m) => {
82            #[allow(clippy::cast_precision_loss)]
83            let kb = m.len() as f64 / 1024.0;
84            if kb < 1024.0 {
85                format!("{kb:.0} KB")
86            } else {
87                format!("{:.1} MB", kb / 1024.0)
88            }
89        }
90        Err(_) => "(unknown size)".to_string(),
91    };
92
93    println!("Done. {output}  {size}");
94}