[READ-ONLY] Mirror of https://github.com/probablykasper/redlux. AAC decoder for MPEG-4 and AAC files, with rodio support
aac audio decoder mp4 package
0

Configure Feed

Select the types of activity you want to include in your feed.

Add README and documentation

Kasper (Feb 1, 2021, 11:50 PM +0100) c938a7de f21e7b1b

+97 -57
+45
README.md
··· 1 + # redlux 2 + 3 + AAC decoder for MPEG-4 (MP4, M4A etc) and AAC files. Supports rodio. 4 + 5 + [![Crates.io](https://img.shields.io/crates/v/redlux.svg)](https://crates.io/crates/redlux) 6 + [![Documentation](https://docs.rs/redlux/badge.svg)](https://docs.rs/redlux) 7 + 8 + ## Caveats 9 + 1. It only decodes the first AAC track it finds in an MPEG-4 container. 10 + 2. Not sure if MPEG files with CRC are supported. 11 + 3. According to [this MultimediaWiki page](https://wiki.multimedia.cx/index.php/ADTS), 13 bits of the ADTS header is for specifying the frame length, and this number must include the ADTS header itself. For 8 channel audio, I would assume the frame length could be 8192 bytes, and if we add the header bytes on top of that, it would exceed what 13 bits can carry. Is this a potential issue? No idea. 12 + 13 + ## Dev instructions 14 + 15 + ### Get started 16 + 17 + Install [Rust](https://www.rust-lang.org). 18 + 19 + Run tests: 20 + ``` 21 + cargo test 22 + ``` 23 + 24 + Build: 25 + ``` 26 + cargo build 27 + ``` 28 + 29 + ### Releasing a new version 30 + 31 + 1. Update `CHANGELOG.md` 32 + 2. Bump the version number in `Cargo.toml` 33 + 3. Run `cargo test` 34 + 4. Commit and tag in format `v#.#.#` 35 + 5. Publish on crates.io: 36 + 1. Login by running `cargo login` and following the instructions 37 + 2. Test publish to ensure there are no errors/warnings 38 + ``` 39 + cargo publish --dry-run 40 + ``` 41 + 3. Publish 42 + ``` 43 + cargo publish 44 + ``` 45 + 6. Create GitHub release with release notes
+52 -57
src/lib.rs
··· 1 + //! AAC decoder for MPEG-4 (MP4, M4A etc) and AAC files. Supports rodio. 1 2 use fdk_aac::dec::{Decoder as AacDecoder, DecoderError, Transport}; 2 - use std::error; 3 - use std::fmt; 4 3 use std::io::{Read, Seek}; 5 4 use std::time::Duration; 5 + use std::{error, fmt, io}; 6 6 7 7 pub mod adts; 8 8 9 + /// Redlux error 9 10 #[derive(Debug)] 10 11 pub enum Error { 11 12 /// Error reading header of file ··· 19 20 TrackDecodingError(DecoderError), 20 21 /// Error getting samples 21 22 SamplesError, 23 + /// Error from the underlying reader R 24 + ReaderError(io::Error), 22 25 } 23 26 24 27 impl error::Error for Error {} ··· 29 32 } 30 33 } 31 34 35 + /// File container format 32 36 pub enum Format { 33 37 Mp4, 34 38 Aac, 35 39 } 36 40 41 + /// Underlying reader 37 42 pub enum Reader<R> { 38 43 Mp4Reader(mp4::Mp4Reader<R>), 39 44 AacReader(R), ··· 51 56 current_pcm: Vec<i16>, 52 57 track_id: u32, 53 58 position: u32, 54 - pub decoder_error: Option<Error>, 59 + /// If there's an error while iterating over the Decoder, that error is added here 60 + pub iter_error: Option<Error>, 55 61 } 56 62 57 63 impl<R> Decoder<R> 58 64 where 59 65 R: Read + Seek, 60 66 { 67 + /// Create from an aac buffer 61 68 pub fn new_aac(reader: R) -> Self { 62 69 let aac_decoder = AacDecoder::new(Transport::Adts); 63 70 let aac_decoder = Decoder { ··· 69 76 current_pcm: Vec::new(), 70 77 track_id: 0, 71 78 position: 1, 72 - decoder_error: None, 79 + iter_error: None, 73 80 }; 74 81 return aac_decoder; 75 82 } 83 + /// Create from an mpeg buffer 76 84 pub fn new_mpeg4(reader: R, size: u64) -> Result<Self, Error> { 77 85 let aac_decoder = AacDecoder::new(Transport::Adts); 78 86 let mp4 = mp4::Mp4Reader::read_header(reader, size).or(Err(Error::FileHeaderError))?; ··· 103 111 current_pcm: Vec::new(), 104 112 track_id: track_id, 105 113 position: 1, 106 - decoder_error: None, 114 + iter_error: None, 107 115 }); 108 116 } 109 117 None => { ··· 126 134 pub fn total_duration(&self) -> Option<Duration> { 127 135 return None; 128 136 } 129 - } 130 - 131 - impl<R> Iterator for Decoder<R> 132 - where 133 - R: Read + Seek, 134 - { 135 - type Item = i16; 136 - fn next(&mut self) -> Option<i16> { 137 + /// Consume and return the next sample, or None when finished 138 + pub fn decode_next_sample(&mut self) -> Result<Option<i16>, Error> { 137 139 if self.current_pcm_index == self.current_pcm.len() { 138 140 let mut pcm = vec![0; 8192]; 139 141 let result = match self.aac_decoder.decode_frame(&mut pcm) { ··· 142 144 // mp4 143 145 Reader::Mp4Reader(mp4_reader) => { 144 146 let sample_result = mp4_reader.read_sample(self.track_id, self.position); 145 - let sample = match sample_result { 146 - Ok(sample) => sample?, // None if EOF 147 - Err(_) => { 148 - self.decoder_error = Some(Error::SamplesError); 149 - return None; 150 - } 147 + let sample_opt = sample_result.or(Err(Error::SamplesError))?; 148 + let sample = match sample_opt { 149 + Some(sample) => sample, 150 + None => return Ok(None), // EOF 151 151 }; 152 152 let tracks = mp4_reader.tracks(); 153 - let track = match tracks.get(self.track_id as usize - 1) { 154 - Some(track) => track, 155 - None => { 156 - self.decoder_error = Some(Error::TrackNotFound); 157 - return None; 158 - } 159 - }; 160 - let object_type = match track.audio_profile() { 161 - Ok(value) => value, 162 - Err(_) => { 163 - self.decoder_error = Some(Error::TrackReadingError); 164 - return None; 165 - } 166 - }; 167 - let sample_freq_index = match track.sample_freq_index() { 168 - Ok(value) => value, 169 - Err(_) => { 170 - self.decoder_error = Some(Error::TrackReadingError); 171 - return None; 172 - } 173 - }; 174 - let channel_config = match track.channel_config() { 175 - Ok(value) => value, 176 - Err(_) => { 177 - self.decoder_error = Some(Error::TrackReadingError); 178 - return None; 179 - } 180 - }; 153 + let track = tracks 154 + .get(self.track_id as usize - 1) 155 + .ok_or(Error::TrackNotFound)?; 156 + let object_type = track.audio_profile().or(Err(Error::TrackReadingError))?; 157 + let sample_freq_index = track 158 + .sample_freq_index() 159 + .or(Err(Error::TrackReadingError))?; 160 + let channel_config = track.channel_config().or(Err(Error::TrackReadingError))?; 181 161 let adts_header = adts::construct_adts_header( 182 162 object_type, 183 163 sample_freq_index, ··· 194 174 let mut new_bytes = vec![0; 8192 - old_bytes_len]; 195 175 let bytes_read = match aac_reader.read(&mut new_bytes) { 196 176 Ok(bytes_read) => bytes_read, 197 - Err(_) => return None, 177 + Err(err) => return Err(Error::ReaderError(err)), 198 178 }; 199 179 if bytes_read == 0 { 200 - return None; 180 + return Ok(None); // EOF 201 181 } 202 182 // aac files already have adts headers 203 183 self.bytes.extend(new_bytes); ··· 205 185 } 206 186 let bytes_filled = match self.aac_decoder.fill(&self.bytes) { 207 187 Ok(bytes_filled) => bytes_filled, 208 - Err(_) => return None, 188 + Err(err) => return Err(Error::TrackDecodingError(err)), 209 189 }; 210 190 self.bytes = self.bytes[bytes_filled..].to_vec(); 211 191 self.aac_decoder.decode_frame(&mut pcm) 212 192 } 213 193 val => val, 214 194 }; 215 - match result { 216 - Ok(_) => {} 217 - Err(err) => { 218 - self.decoder_error = Some(Error::TrackDecodingError(err)); 219 - return None; 220 - } 195 + if let Err(err) = result { 196 + return Err(Error::TrackDecodingError(err)); 221 197 } 222 198 let decoded_frame_size = self.aac_decoder.decoded_frame_size(); 223 199 if decoded_frame_size < pcm.len() { ··· 228 204 } 229 205 let value = self.current_pcm[self.current_pcm_index]; 230 206 self.current_pcm_index += 1; 231 - return Some(value); 207 + return Ok(Some(value)); 208 + } 209 + } 210 + 211 + impl<R> Iterator for Decoder<R> 212 + where 213 + R: Read + Seek, 214 + { 215 + type Item = i16; 216 + /// Runs decode_next_sample and returns the sample from that. Once the 217 + /// iterator is finished, it returns None. If there's an error, it's added 218 + /// to the iter_error error. 219 + fn next(&mut self) -> Option<i16> { 220 + match self.decode_next_sample() { 221 + Ok(sample) => sample, 222 + Err(err) => { 223 + self.iter_error = Some(err); 224 + return None; 225 + } 226 + } 232 227 } 233 228 } 234 229