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