Skip to main content

file_info/
file_info.rs

1use std::env;
2use std::fs;
3use std::io;
4use std::path::Path;
5
6use xaac_rs::{Decoder, DecoderConfig, SbrMode};
7
8fn main() -> Result<(), Box<dyn std::error::Error>> {
9    let input = env::args().nth(1).ok_or_else(usage)?;
10    let input_path = Path::new(&input);
11    let data = fs::read(input_path)?;
12
13    let mut decoder = Decoder::new(DecoderConfig::default())?;
14    let version = decoder.version().clone();
15
16    println!("file: {}", input_path.display());
17    println!("size: {} bytes", data.len());
18    println!("decoder: {} {}", version.name, version.version);
19    println!("decoder input capacity: {} bytes", decoder.input_capacity());
20
21    if data.is_empty() {
22        println!("stream info: file is empty");
23        return Ok(());
24    }
25
26    let probe_len = data.len();
27    let probe = &data;
28
29    match decoder.probe_stream_info(probe) {
30        Ok(info) => {
31            println!("probe bytes: {}", probe_len);
32            println!("sample rate: {} Hz", info.sample_rate);
33            println!("channels: {}", info.channels);
34            println!("channel mask: 0x{:x}", info.channel_mask);
35            println!("channel mode: {:?}", info.channel_mode);
36            println!("pcm word size: {} bits", info.pcm_word_size);
37            println!("audio object type: {}", info.audio_object_type);
38            println!("sbr mode: {:?}", info.sbr_mode);
39            println!("drc active: {}", info.drc_active);
40            println!(
41                "drc target loudness: {}",
42                info.drc_target_loudness
43                    .map_or_else(|| "unavailable".to_string(), |value| value.to_string())
44            );
45            println!(
46                "drc loudness norm: {}",
47                info.drc_loudness_norm
48                    .map_or_else(|| "unavailable".to_string(), |value| value.to_string())
49            );
50            println!(
51                "loudness leveling: {}",
52                info.loudness_leveling
53                    .map_or_else(|| "unavailable".to_string(), |value| value.to_string())
54            );
55            println!(
56                "preroll frames: {}",
57                info.preroll_frames
58                    .map_or_else(|| "unavailable".to_string(), |value| value.to_string())
59            );
60            println!(
61                "gain payload bytes: {}",
62                info.gain_payload_len
63                    .map_or_else(|| "unavailable".to_string(), |value| value.to_string())
64            );
65            match estimate_adts_bitrate(&data, info.sample_rate, info.sbr_mode) {
66                Some(bitrate) => println!("bit rate: {} bps", bitrate),
67                None => println!("bit rate: unavailable"),
68            }
69        }
70        Err(err) => {
71            println!("stream info: unavailable");
72            println!("decoder error: {err}");
73            println!("probe bytes: {}", probe_len);
74        }
75    }
76
77    Ok(())
78}
79
80fn usage() -> Box<dyn std::error::Error> {
81    Box::new(io::Error::new(
82        io::ErrorKind::InvalidInput,
83        "usage: cargo run --example file_info -- <input.aac>",
84    ))
85}
86
87fn estimate_adts_bitrate(data: &[u8], sample_rate: u32, sbr_mode: SbrMode) -> Option<u32> {
88    if sample_rate == 0 {
89        return None;
90    }
91
92    let mut offset = 0usize;
93    let mut frames = 0u64;
94    let mut payload_bytes = 0u64;
95
96    while offset + 7 <= data.len() {
97        if data[offset] != 0xff || (data[offset + 1] & 0xf0) != 0xf0 {
98            return None;
99        }
100
101        let frame_length = (((data[offset + 3] & 0x03) as usize) << 11)
102            | ((data[offset + 4] as usize) << 3)
103            | (((data[offset + 5] & 0xe0) as usize) >> 5);
104
105        if frame_length < 7 || offset + frame_length > data.len() {
106            return None;
107        }
108
109        frames += 1;
110        payload_bytes += frame_length as u64;
111        offset += frame_length;
112    }
113
114    if frames == 0 || offset != data.len() {
115        return None;
116    }
117
118    let samples_per_frame = match sbr_mode {
119        SbrMode::Enabled => 2048u64,
120        SbrMode::Esbr => 4096u64,
121        _ => 1024u64,
122    };
123    let total_samples = frames.checked_mul(samples_per_frame)?;
124    let bits = payload_bytes.checked_mul(8)?;
125    let bitrate = bits
126        .checked_mul(sample_rate as u64)?
127        .checked_div(total_samples)?;
128    u32::try_from(bitrate).ok()
129}