extern crate claxon;
extern crate hound;
extern crate mp4parse;
use std::env;
use std::fs::File;
use std::io::Seek;
use std::io;
use std::path::Path;
use claxon::metadata::read_metadata_block;
use claxon::metadata::StreamInfo;
use hound::{WavSpec, WavWriter};
use mp4parse::CodecType;
fn decode_file(fname: &Path) {
let file = File::open(fname).expect("failed to open mp4 file");
let mut bufread = io::BufReader::new(file);
let mut context = mp4parse::MediaContext::new();
mp4parse::read_mp4(&mut bufread, &mut context).expect("failed to decode mp4");
for track in &context.tracks {
if track.codec_type != CodecType::FLAC {
continue
}
let streaminfo = get_streaminfo(track).expect("missing streaminfo");
let spec = WavSpec {
channels: streaminfo.channels as u16,
sample_rate: streaminfo.sample_rate,
bits_per_sample: streaminfo.bits_per_sample as u16,
sample_format: hound::SampleFormat::Int,
};
let fname_wav = fname.with_extension("wav");
let opt_wav_writer = WavWriter::create(fname_wav, spec);
let mut wav_writer = opt_wav_writer.expect("failed to create wav file");
let chunk_offsets = &track.stco.as_ref().expect("missing chunk offset box").offsets;
let chunk_samples = &track.stsc.as_ref().expect("missing sample to chunk box").samples;
let mut samples_iter = chunk_samples.iter();
let mut next_samples = samples_iter.next();
let mut samples_per_chunk = 0;
for (i, offset) in chunk_offsets.iter().enumerate() {
bufread.seek(io::SeekFrom::Start(*offset)).expect("failed to seek to chunk");
next_samples = next_samples.and_then(|ns| {
if ns.first_chunk == 1 + i as u32 {
samples_per_chunk = ns.samples_per_chunk;
samples_iter.next()
} else {
Some(ns)
}
});
bufread = decode_frames(bufread, &streaminfo, samples_per_chunk, &mut wav_writer);
}
break
}
}
fn get_streaminfo(track: &mp4parse::Track) -> Option<StreamInfo> {
use mp4parse::{AudioCodecSpecific, SampleEntry};
use claxon::metadata::MetadataBlock;
let audio_entry = match &track.data {
&Some(SampleEntry::Audio(ref ae)) => ae,
_ => panic!("expected to find audio entry in FLAC track"),
};
let flac_box = match &audio_entry.codec_specific {
&AudioCodecSpecific::FLACSpecificBox(ref fb) => fb,
_ => return None,
};
for raw_block in &flac_box.blocks {
let len = raw_block.data.len() as u32;
let mut cursor = io::Cursor::new(&raw_block.data);
let result = read_metadata_block(&mut cursor, raw_block.block_type, len);
match result.expect("failed to decode metadata block") {
MetadataBlock::StreamInfo(si) => return Some(si),
_ => {}
}
}
None
}
fn decode_frames<R, W>(input: R,
streaminfo: &StreamInfo,
num_frames: u32,
wav_writer: &mut WavWriter<W>)
-> R
where R: io::Read, W: io::Write + io::Seek {
let buffered_reader = claxon::input::BufferedReader::new(input);
let mut frame_reader = claxon::frame::FrameReader::new(buffered_reader);
let mut buffer = Vec::with_capacity(streaminfo.max_block_size as usize *
streaminfo.channels as usize);
for _ in 0..num_frames {
let result = frame_reader.read_next_or_eof(buffer);
let block = result.expect("failed to decode frame").expect("unexpected EOF");
for (sl, sr) in block.stereo_samples() {
wav_writer.write_sample(sl).expect("failed to write wav file");
wav_writer.write_sample(sr).expect("failed to write wav file");
}
buffer = block.into_buffer();
}
frame_reader.into_inner().into_inner()
}
fn main() {
for fname in env::args().skip(1) {
decode_file(&Path::new(&fname));
}
}