1 // Claxon -- A FLAC decoding library in Rust
2 // Copyright 2017 Ruud van Asseldonk
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // A copy of the License has been included in the root of the repository.
7
8 // This file contains a minimal example of using Claxon with mp4parse and Hound
9 // to decode a flac stream inside an MP4 (.mp4 or .m4a) container to a wav file.
10
11 extern crate claxon;
12 extern crate hound;
13 extern crate mp4parse;
14
15 use std::env;
16 use std::fs::File;
17 use std::io::Seek;
18 use std::io;
19 use std::path::Path;
20
21 use claxon::metadata::read_metadata_block;
22 use claxon::metadata::StreamInfo;
23 use hound::{WavSpec, WavWriter};
24 use mp4parse::CodecType;
25
decode_file(fname: &Path)26 fn decode_file(fname: &Path) {
27 // Create a file to read the mp4 structure from. We will later need to seek
28 // in this file to get to the parts that contain FLAC data.
29 let file = File::open(fname).expect("failed to open mp4 file");
30 let mut bufread = io::BufReader::new(file);
31
32 // Parse the mp4 metadata.
33 let mut context = mp4parse::MediaContext::new();
34 mp4parse::read_mp4(&mut bufread, &mut context).expect("failed to decode mp4");
35
36 // An MP4 file contains one or more tracks. A track can contain a single
37 // FLAC stream. Iterate over those.
38 for track in &context.tracks {
39 if track.codec_type != CodecType::FLAC {
40 continue
41 }
42
43 let streaminfo = get_streaminfo(track).expect("missing streaminfo");
44
45 // Build a wav writer to write the decoded track to a wav file.
46 let spec = WavSpec {
47 channels: streaminfo.channels as u16,
48 sample_rate: streaminfo.sample_rate,
49 bits_per_sample: streaminfo.bits_per_sample as u16,
50 sample_format: hound::SampleFormat::Int,
51 };
52
53 let fname_wav = fname.with_extension("wav");
54 let opt_wav_writer = WavWriter::create(fname_wav, spec);
55 let mut wav_writer = opt_wav_writer.expect("failed to create wav file");
56
57 // The data in an MP4 file is split into "chunks", for which we can get
58 // the file offset where the chunk starts. Every chunk contains one or
59 // more "samples", which is one FLAC frame. These frames are all stored
60 // adjacently in the chunk, so we can read them with a fingle frame
61 // reader.
62
63 // The "stco" in mp4parse's Track stands for "Chunk Offset Box", "stsc"
64 // stands for "Sample to Chunk Box", which actually tells per chunk how
65 // many samples it contains, not per sample in which chunk it is.
66 let chunk_offsets = &track.stco.as_ref().expect("missing chunk offset box").offsets;
67 let chunk_samples = &track.stsc.as_ref().expect("missing sample to chunk box").samples;
68
69 let mut samples_iter = chunk_samples.iter();
70 let mut next_samples = samples_iter.next();
71 let mut samples_per_chunk = 0;
72
73 // Iterate over all chunks in this track. We need all chunks, and every
74 // chunk is present in the chunk offset box.
75 for (i, offset) in chunk_offsets.iter().enumerate() {
76 bufread.seek(io::SeekFrom::Start(*offset)).expect("failed to seek to chunk");
77
78 // For some chunks, the "Sample to Chunk Box" stores details about
79 // how many "samples" (FLAC frames) there are per chunk. When there
80 // is no such data for a chunk, the samples per chunk is the same as
81 // for the previous chunk.
82 next_samples = next_samples.and_then(|ns| {
83 // The first_chunk field is a 1-based index, not 0-based.
84 if ns.first_chunk == 1 + i as u32 {
85 samples_per_chunk = ns.samples_per_chunk;
86 samples_iter.next()
87 } else {
88 Some(ns)
89 }
90 });
91
92 bufread = decode_frames(bufread, &streaminfo, samples_per_chunk, &mut wav_writer);
93 }
94
95 // Stop iterating over tracks; if there are more FLAC tracks, we would
96 // overwrite the previously written output. (This could be avoided by
97 // picking a unique file name for every track, or by asking the user
98 // which track to decode.)
99 break
100 }
101 }
102
103 /// Decode the metadata blocks until the streaminfo block is found.
get_streaminfo(track: &mp4parse::Track) -> Option<StreamInfo>104 fn get_streaminfo(track: &mp4parse::Track) -> Option<StreamInfo> {
105 use mp4parse::{AudioCodecSpecific, SampleEntry};
106 use claxon::metadata::MetadataBlock;
107
108 let audio_entry = match &track.data {
109 &Some(SampleEntry::Audio(ref ae)) => ae,
110 _ => panic!("expected to find audio entry in FLAC track"),
111 };
112
113 let flac_box = match &audio_entry.codec_specific {
114 &AudioCodecSpecific::FLACSpecificBox(ref fb) => fb,
115 _ => return None,
116 };
117
118 for raw_block in &flac_box.blocks {
119 let len = raw_block.data.len() as u32;
120 let mut cursor = io::Cursor::new(&raw_block.data);
121 let result = read_metadata_block(&mut cursor, raw_block.block_type, len);
122 match result.expect("failed to decode metadata block") {
123 MetadataBlock::StreamInfo(si) => return Some(si),
124 _ => {}
125 }
126 }
127
128 None
129 }
130
131 /// Decode a number of FLAC frames. Takes an input `io::Read` and returns it.
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::Seek132 fn decode_frames<R, W>(input: R,
133 streaminfo: &StreamInfo,
134 num_frames: u32,
135 wav_writer: &mut WavWriter<W>)
136 -> R
137 where R: io::Read, W: io::Write + io::Seek {
138 // The simplest way to read frames now, is unfortunately to double buffer.
139 // (The input `R` is a buffered reader.) This might be avoided by not
140 // wrapping the original file in an `io::BufReader`, but in a Claxon
141 // `BufferedReader`. It would have to implement `io::Read` then.
142 let buffered_reader = claxon::input::BufferedReader::new(input);
143 let mut frame_reader = claxon::frame::FrameReader::new(buffered_reader);
144 let mut buffer = Vec::with_capacity(streaminfo.max_block_size as usize *
145 streaminfo.channels as usize);
146
147 for _ in 0..num_frames {
148 // TODO There should be a read_next method too that does not tolerate
149 // EOF.
150 let result = frame_reader.read_next_or_eof(buffer);
151 let block = result.expect("failed to decode frame").expect("unexpected EOF");
152
153 // TODO: Here we assume that we are decoding a stereo stream, which
154 // is wrong, but very convenient, as there is no interleaved sample
155 // iterator for `Block`. One should be added.
156 for (sl, sr) in block.stereo_samples() {
157 wav_writer.write_sample(sl).expect("failed to write wav file");
158 wav_writer.write_sample(sr).expect("failed to write wav file");
159 }
160
161 buffer = block.into_buffer();
162 }
163
164 // Strip off the frame reader and buffered reader to get back the original
165 // reader.
166 frame_reader.into_inner().into_inner()
167 }
168
main()169 fn main() {
170 for fname in env::args().skip(1) {
171 decode_file(&Path::new(&fname));
172 }
173 }
174