Created
May 16, 2022 20:24
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Code is mostly taken from this https://github.com/angelcam/rust-ac-ffmpeg/blob/master/examples/decoding.rs