Skip to content

Instantly share code, notes, and snippets.

@Ryu1845
Created May 16, 2022 20:24
Show Gist options
  • Save Ryu1845/d01934f6782c3976e24f198562a5c394 to your computer and use it in GitHub Desktop.
Save Ryu1845/d01934f6782c3976e24f198562a5c394 to your computer and use it in GitHub Desktop.
Decode audio file to wav f32 in rust using ac ffmpeg and hound
use ac_ffmpeg::{
codec::{
audio::{AudioDecoder, AudioResampler, SampleFormat},
Decoder,
},
format::{
demuxer::{Demuxer, DemuxerWithStreamInfo},
io::IO,
},
Error,
};
use clap::{App, Arg};
use hound;
use std::{convert::TryInto, fs::File};
use std::{ops::Deref, str::FromStr};
/// Open a given input file.
fn open_input(path: &str) -> Result<DemuxerWithStreamInfo<File>, Error> {
let input = File::open(path)
.map_err(|err| Error::new(format!("unable to open input file {}: {}", path, err)))?;
let io = IO::from_seekable_read_stream(input);
Demuxer::builder()
.build(io)?
.find_stream_info(None)
.map_err(|(_, err)| err)
}
/// Decode all video frames from the first video stream and print their
/// presentation timestamps.
fn print_video_frame_info(input: &str) -> Result<(), Error> {
let mut demuxer = open_input(input)?;
let (stream_index, (stream, _)) = demuxer
.streams()
.iter()
.map(|stream| (stream, stream.codec_parameters()))
.enumerate()
.find(|(_, (_, params))| params.is_audio_codec())
.ok_or_else(|| Error::new("no video stream"))?;
let mut decoder = AudioDecoder::from_stream(stream)?.build()?;
let codec_param = stream.codec_parameters();
let audio_param = codec_param.as_audio_codec_parameters().unwrap();
let mut resampler = AudioResampler::builder()
.source_sample_format(audio_param.sample_format())
.target_sample_format(SampleFormat::from_str("flt").expect("can't find sample format"))
.source_channel_layout(audio_param.channel_layout())
.target_channel_layout(audio_param.channel_layout())
.source_sample_rate(audio_param.sample_rate())
.target_sample_rate(audio_param.sample_rate())
.build()?;
let spec = hound::WavSpec {
channels: 1,
sample_rate: 44100,
bits_per_sample: 32,
sample_format: hound::SampleFormat::Float,
};
// process data
let mut writer = hound::WavWriter::create("test.wav", spec).unwrap();
while let Some(packet) = demuxer.take()? {
if packet.stream_index() != stream_index {
continue;
}
decoder.push(packet)?;
while let Some(frame) = decoder.take()? {
resampler.push(frame)?;
while let Some(frame) = resampler.take()? {
let planes = frame.planes();
let data = &mut planes.deref().get(0).unwrap().data();
while data.len() > 0 {
let (int_bytes, rest) = data.split_at(std::mem::size_of::<f32>());
*data = rest;
let sample = f32::from_le_bytes(int_bytes.try_into().unwrap());
writer.write_sample(sample).unwrap();
}
}
}
}
decoder.flush()?;
resampler.flush()?;
while let Some(_) = decoder.take()? {
while let Some(frame) = resampler.take()? {
let planes = frame.planes();
let data = &mut planes.deref().get(0).unwrap().data();
while data.len() > 0 {
let (int_bytes, rest) = data.split_at(std::mem::size_of::<f32>());
*data = rest;
let sample = f32::from_le_bytes(int_bytes.try_into().unwrap());
writer.write_sample(sample).unwrap();
}
}
}
writer.finalize().unwrap();
Ok(())
}
fn main() {
let matches = App::new("decoding")
.arg(
Arg::with_name("input")
.required(true)
.takes_value(true)
.value_name("INPUT")
.help("Input file"),
)
.get_matches();
let input_filename = matches.value_of("input").unwrap();
if let Err(err) = print_video_frame_info(input_filename) {
eprintln!("ERROR: {}", err);
}
}
@Ryu1845
Copy link
Author

Ryu1845 commented May 16, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment