]>
Commit | Line | Data |
---|---|---|
ed91af4b KS |
1 | use std::collections::VecDeque; |
2 | ||
3 | use nihav_core::codecs::*; | |
4 | use nihav_core::io::bitwriter::*; | |
5 | use nihav_codec_support::dsp::dct::*; | |
6 | use nihav_codec_support::dsp::fft::*; | |
7 | use super::binkauddata::*; | |
8 | ||
9 | trait WriteBinkFloat { | |
10 | fn write_float(&mut self, val: f32); | |
11 | } | |
12 | ||
13 | impl WriteBinkFloat for BitWriter { | |
14 | fn write_float(&mut self, val: f32) { | |
15 | let bits = val.to_bits(); | |
16 | let sign = bits >> 31; | |
17 | let nexp = ((bits >> 23).wrapping_sub(0x7E)) & 0x1F; | |
18 | let mant = bits & ((1 << 23) - 1); | |
19 | self.write(nexp, 5); | |
20 | self.write(mant, 23); | |
21 | self.write(sign, 1); | |
22 | } | |
23 | } | |
24 | ||
25 | #[derive(Default)] | |
26 | struct RateControl { | |
27 | quality: u8, | |
28 | bitrate: u32, | |
29 | bitpool: u32, | |
30 | spos: u32, | |
31 | srate: u32, | |
32 | blk_size: u32, | |
33 | lambda: f32, | |
34 | } | |
35 | ||
36 | impl RateControl { | |
37 | fn init(&mut self, quality: u8, bitrate: u32, srate: u32, blk_size: u32) { | |
38 | self.quality = quality; | |
39 | self.bitrate = bitrate; | |
40 | self.bitpool = bitrate; | |
41 | self.srate = srate; | |
42 | self.spos = 0; | |
43 | self.blk_size = blk_size; | |
44 | self.lambda = if self.quality != 0 { | |
45 | ((100 - self.quality) as f32) / 20.0 | |
46 | } else { | |
47 | 1.0 | |
48 | }; | |
49 | } | |
50 | fn get_tgt_size(&self) -> usize { | |
51 | if self.bitrate > 0 { | |
52 | (self.bitpool * self.blk_size / (self.srate - self.spos)) as usize | |
53 | } else { | |
54 | 0 | |
55 | } | |
56 | } | |
57 | fn update(&mut self, real_size: usize) { | |
58 | if self.bitrate == 0 { | |
59 | return; | |
60 | } | |
61 | ||
62 | let tgt_size = self.get_tgt_size(); | |
63 | ||
64 | if real_size < tgt_size - tgt_size / 8 { | |
65 | self.lambda -= 0.2; | |
66 | if self.lambda < 0.0 { | |
67 | self.lambda = 0.0; | |
68 | } | |
69 | } | |
70 | if real_size > tgt_size + tgt_size / 8 { | |
71 | self.lambda += 0.5; | |
72 | } | |
73 | ||
74 | self.spos += self.blk_size; | |
75 | while self.spos >= self.srate { | |
76 | self.spos -= self.srate; | |
77 | self.bitpool += self.bitrate; | |
78 | } | |
79 | self.bitpool = self.bitpool.saturating_sub(real_size as u32); | |
80 | } | |
81 | } | |
82 | ||
83 | #[derive(Default)] | |
84 | struct SampleBuffer { | |
85 | samples: Vec<f32>, | |
86 | read_pos: usize, | |
87 | frame_len: usize, | |
88 | step_size: usize, | |
89 | } | |
90 | ||
91 | impl SampleBuffer { | |
92 | fn reset(&mut self) { | |
93 | self.samples.clear(); | |
94 | self.read_pos = 0; | |
95 | } | |
96 | fn num_avail(&self) -> usize { self.samples.len() - self.read_pos } | |
97 | fn add_mono(&mut self, src: &[f32]) { | |
98 | self.samples.extend_from_slice(src); | |
99 | } | |
100 | fn add_stereo(&mut self, left: &[f32], right: &[f32]) { | |
101 | for (&l, &r) in left.iter().zip(right.iter()) { | |
102 | self.samples.push(l); | |
103 | self.samples.push(r); | |
104 | } | |
105 | } | |
106 | fn norm(&mut self) { | |
107 | if self.read_pos == self.samples.len() { | |
108 | self.read_pos = 0; | |
109 | self.samples.clear(); | |
110 | } else if self.read_pos * 2 >= self.samples.len() { | |
111 | let len = self.num_avail(); | |
112 | let (head, tail) = self.samples.split_at_mut(self.read_pos); | |
113 | head[..tail.len()].copy_from_slice(tail); | |
114 | self.read_pos = 0; | |
115 | self.samples.truncate(len); | |
116 | } | |
117 | } | |
118 | fn read_frame(&self, dst: &mut [f32]) { | |
119 | let src = &self.samples[self.read_pos..]; | |
120 | let len = dst.len().min(src.len()).min(self.frame_len); | |
121 | dst[..len].copy_from_slice(&src[..len]); | |
122 | let ovl_len = self.frame_len - self.step_size; | |
123 | for (i, sample) in dst[..ovl_len].iter_mut().enumerate() { | |
124 | *sample *= (i as f32) / (ovl_len as f32); | |
125 | } | |
126 | for (i, sample) in dst[self.step_size..][..ovl_len].iter_mut().rev().enumerate() { | |
127 | *sample *= (i as f32) / (ovl_len as f32); | |
128 | } | |
129 | } | |
130 | fn consume_frame(&mut self) { | |
131 | self.read_pos += self.step_size; | |
132 | if self.read_pos > self.samples.len() { | |
133 | self.read_pos = self.samples.len(); | |
134 | } | |
135 | self.norm(); | |
136 | } | |
137 | } | |
138 | ||
139 | fn find_quant(src: &[f32], quants: &[f32; 96], lambda: f32) -> usize { | |
140 | let maxval = src.iter().fold(0.0f32, |acc, &a| acc.max(a.abs())); | |
141 | ||
142 | let mut best = 0; | |
143 | let mut best_dist = maxval * maxval * (src.len() as f32); | |
144 | for (i, &q) in quants.iter().enumerate() { | |
145 | if maxval / q > 32767.0 { continue; } | |
146 | if q > maxval * 2.0 { break; } | |
147 | let mut dist = 0.0; | |
148 | let mut maxqv = 0.0f32; | |
149 | let mut signs = 0u32; | |
150 | for &el in src.iter() { | |
151 | let qval = (el.abs() / q).round(); | |
152 | let iqval = qval * q; | |
153 | dist += (el.abs() - iqval) * (el.abs() - iqval); | |
154 | maxqv = maxqv.max(qval); | |
155 | if qval > 0.0 { | |
156 | signs += 1; | |
157 | } | |
158 | } | |
159 | let bits = if maxqv > 0.0 { | |
160 | maxqv.log2().ceil() * (src.len() as f32) + (signs as f32) | |
161 | } else { 0.0 }; | |
162 | let metric = (dist + 1.0).log2() * 10.0 + bits * lambda; | |
163 | if metric < best_dist { | |
164 | best_dist = metric; | |
165 | best = i; | |
166 | } | |
167 | } | |
168 | best | |
169 | } | |
170 | ||
171 | enum Transform { | |
172 | None, | |
173 | DCT(DCT), | |
174 | RDFT(RDFT), | |
175 | } | |
176 | ||
177 | struct BinkAudioEncoder { | |
178 | stream: Option<NAStreamRef>, | |
179 | version: char, | |
180 | stereo: bool, | |
181 | ||
182 | frm_size: usize, | |
183 | frame_bits: u8, | |
184 | overlap_size: usize, | |
185 | ||
186 | apts: u64, | |
187 | use_dct: bool, | |
188 | preload: usize, | |
189 | ||
190 | samples: SampleBuffer, | |
191 | transform: Transform, | |
192 | scale: f32, | |
193 | quants: [f32; 96], | |
194 | num_bands: usize, | |
195 | bands: [usize; MAX_BANDS + 1], | |
196 | first_frame: bool, | |
197 | sample_step: usize, | |
198 | leftover: usize, | |
199 | ||
200 | tmp: Vec<f32>, | |
201 | ||
202 | packets: VecDeque<NAPacket>, | |
203 | rc: RateControl, | |
204 | ||
205 | print_stats: bool, | |
206 | blk_bits: usize, | |
207 | nblks: usize, | |
208 | nsamples: usize, | |
209 | nframes: usize, | |
210 | qstats: [usize; 96], | |
211 | bstats: [usize; 16], | |
212 | } | |
213 | ||
214 | impl BinkAudioEncoder { | |
215 | fn new(use_dct: bool) -> Self { | |
216 | Self { | |
217 | stream: None, | |
218 | version: 'b', | |
219 | stereo: false, | |
220 | ||
221 | frm_size: 0, | |
222 | frame_bits: 0, | |
223 | overlap_size: 0, | |
224 | ||
225 | apts: 0, | |
226 | use_dct, | |
227 | preload: 16, | |
228 | ||
229 | samples: SampleBuffer::default(), | |
230 | transform: Transform::None, | |
231 | scale: 0.0, | |
232 | quants: get_quants_table(), | |
233 | num_bands: 0, | |
234 | bands: [0; MAX_BANDS + 1], | |
235 | first_frame: true, | |
236 | sample_step: 0, | |
237 | leftover: 0, | |
238 | ||
239 | tmp: Vec::new(), | |
240 | ||
241 | packets: VecDeque::new(), | |
242 | rc: RateControl::default(), | |
243 | ||
244 | print_stats: false, | |
245 | blk_bits: 0, | |
246 | nblks: 0, | |
247 | nsamples: 0, | |
248 | nframes: 0, | |
249 | qstats: [0; 96], | |
250 | bstats: [0; 16], | |
251 | } | |
252 | } | |
253 | #[allow(clippy::transmute_ptr_to_ptr)] | |
254 | fn encode_block(&mut self, bw: &mut BitWriter) { | |
255 | match self.transform { | |
256 | Transform::None => unreachable!(), | |
257 | Transform::DCT(ref mut _dct) => unimplemented!(), | |
258 | Transform::RDFT(ref mut rdft) => { | |
259 | unsafe { | |
260 | let buf = std::mem::transmute::<&mut [f32], &mut [FFTComplex]>(self.tmp.as_mut_slice()); | |
261 | rdft.do_rdft_inplace(buf); | |
262 | } | |
263 | }, | |
264 | }; | |
265 | for el in self.tmp.iter_mut() { | |
266 | *el *= self.samples.frame_len as f32; | |
267 | } | |
268 | if self.version == 'b' { | |
269 | bw.write(self.tmp[0].to_bits(), 32); | |
270 | bw.write(self.tmp[1].to_bits(), 32); | |
271 | } else { | |
272 | bw.write_float(self.tmp[0]); | |
273 | bw.write_float(self.tmp[1]); | |
274 | } | |
275 | ||
276 | let mut quants = [0; MAX_BANDS]; | |
277 | for (range, quant) in self.bands.windows(2).take(self.num_bands).zip(quants.iter_mut()) { | |
278 | let region = &mut self.tmp[range[0]..range[1]]; | |
279 | *quant = find_quant(region, &self.quants, self.rc.lambda); | |
280 | for el in region.iter_mut() { | |
281 | *el /= self.quants[*quant]; | |
282 | } | |
283 | } | |
284 | ||
285 | let mut rec_bits = [0u8; 4096]; | |
286 | for (bval, &coef) in rec_bits.iter_mut().zip(self.tmp.iter()).skip(2) { | |
287 | let aval = coef.abs().round() as u32; | |
288 | *bval = if aval > 0 { (32 - aval.leading_zeros()) as u8 } else { 0 }; | |
289 | } | |
290 | ||
291 | for &quant in quants[..self.num_bands].iter() { | |
292 | self.qstats[quant] += 1; | |
293 | } | |
294 | ||
295 | for &quant in quants[..self.num_bands].iter() { | |
296 | bw.write(quant as u32, 8); | |
297 | } | |
298 | if self.version == 'b' { | |
299 | for (coef_reg, bits_reg) in self.tmp[2..].chunks(16).zip(rec_bits[2..].chunks(16)) { | |
300 | let max_bits = bits_reg.iter().fold(0u8, |acc, &a| acc.max(a)).min(15); | |
301 | bw.write(u32::from(max_bits), 4); | |
302 | self.bstats[usize::from(max_bits)] += coef_reg.len(); | |
303 | if max_bits != 0 { | |
304 | for &coef in coef_reg.iter() { | |
305 | let bval = (coef.abs().round() as u32).min((1 << max_bits) - 1); | |
306 | bw.write(bval, max_bits); | |
307 | if bval != 0 { | |
308 | bw.write_bit(coef < 0.0); | |
309 | } | |
310 | } | |
311 | } | |
312 | } | |
313 | } else { | |
314 | unimplemented!(); | |
315 | } | |
316 | } | |
317 | fn encode_packet(&mut self, last: bool) -> EncoderResult<bool> { | |
318 | let nsamples = if last { | |
319 | self.samples.num_avail() | |
320 | } else if self.first_frame { | |
321 | self.preload.max(1) * self.frm_size * if self.stereo { 2 } else { 1 } | |
322 | } else { | |
323 | (self.leftover + self.sample_step) * if self.stereo { 2 } else { 1 } | |
324 | }; | |
325 | ||
326 | let nblocks = if !last { | |
327 | let nblk = (nsamples / self.frm_size).max(1); | |
328 | if self.samples.num_avail() < nblk * self.frm_size + self.overlap_size { | |
329 | return Ok(false); | |
330 | } | |
331 | self.leftover = (self.leftover + self.sample_step) % self.frm_size; | |
332 | nblk | |
333 | } else { | |
334 | (nsamples + self.frm_size - 1) / self.frm_size | |
335 | }; | |
336 | let nsamples = if !last { nblocks * self.frm_size } else { nsamples }; | |
337 | ||
338 | self.nsamples += nsamples >> (self.stereo as u8); | |
339 | self.nframes += 1; | |
340 | ||
341 | let mut bw = BitWriter::new(Vec::new(), BitWriterMode::LE); | |
342 | bw.write((nsamples * 2) as u32, 32); // number of raw audio bytes | |
343 | ||
344 | for _nblk in 0..nblocks { | |
345 | let start = bw.tell(); | |
346 | ||
347 | if last { | |
348 | for el in self.tmp.iter_mut() { | |
349 | *el = 0.0; | |
350 | } | |
351 | } | |
352 | self.samples.read_frame(&mut self.tmp); | |
353 | self.encode_block(&mut bw); | |
354 | while (bw.tell() & 0x1F) != 0 { | |
355 | bw.write0(); | |
356 | } | |
357 | self.samples.consume_frame(); | |
358 | ||
359 | let blk_bits = bw.tell() - start; | |
360 | self.rc.update(blk_bits); | |
361 | self.blk_bits += blk_bits; | |
362 | self.nblks += 1; | |
363 | } | |
364 | ||
365 | if self.first_frame { | |
366 | self.first_frame = false; | |
367 | } | |
368 | ||
369 | let dbuf = bw.end(); | |
370 | let stream = self.stream.clone().unwrap(); | |
371 | let (tb_num, tb_den) = stream.get_timebase(); | |
372 | let ts = NATimeInfo::new(Some(self.apts), None, Some(nsamples as u64), tb_num, tb_den); | |
373 | self.apts += (nsamples / if self.stereo { 2 } else { 1 }) as u64; | |
374 | self.packets.push_back(NAPacket::new(self.stream.clone().unwrap(), ts, true, dbuf)); | |
375 | ||
376 | Ok(true) | |
377 | } | |
378 | } | |
379 | ||
380 | impl NAEncoder for BinkAudioEncoder { | |
381 | fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> { | |
382 | match encinfo.format { | |
383 | NACodecTypeInfo::None => { | |
384 | Ok(EncodeParameters { | |
385 | format: NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_F32P_FORMAT, 512)), | |
386 | ..Default::default() }) | |
387 | }, | |
388 | NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), | |
389 | NACodecTypeInfo::Audio(ainfo) => { | |
390 | let mut outinfo = ainfo; | |
391 | outinfo.channels = if !self.use_dct { 2 } else { ainfo.channels.min(2).max(1) }; | |
392 | outinfo.format = SND_F32P_FORMAT; | |
393 | let mut ofmt = *encinfo; | |
394 | ofmt.format = NACodecTypeInfo::Audio(outinfo); | |
395 | Ok(ofmt) | |
396 | } | |
397 | } | |
398 | } | |
399 | fn get_capabilities(&self) -> u64 { ENC_CAPS_CBR } | |
400 | fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> { | |
401 | match encinfo.format { | |
402 | NACodecTypeInfo::None => Err(EncoderError::FormatError), | |
403 | NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), | |
404 | NACodecTypeInfo::Audio(ainfo) => { | |
405 | if ainfo.format != SND_F32P_FORMAT { | |
406 | return Err(EncoderError::FormatError); | |
407 | } | |
408 | ||
409 | let srate = ainfo.get_sample_rate(); | |
410 | let mut frame_bits = if srate < 22050 { 9 } else if srate < 44100 { 10 } else { 11 }; | |
411 | ||
412 | if self.version < 'i' && self.use_dct { | |
413 | println!("DCT is supported starting from version 'i'"); | |
414 | return Err(EncoderError::FormatError); | |
415 | } | |
416 | ||
417 | if !self.use_dct && self.version != 'b' { | |
418 | frame_bits += 1; | |
419 | } | |
420 | self.frame_bits = frame_bits; | |
421 | self.stereo = ainfo.channels == 2; | |
422 | let mut duration = (1 << frame_bits) - (1 << (frame_bits - 4)); | |
423 | let single = !self.use_dct && self.stereo; // RDFT codes samples interleaved as single buffer | |
424 | if single { | |
425 | duration >>= 1; | |
426 | } | |
427 | ||
428 | self.transform = if !self.use_dct { | |
429 | Transform::RDFT(RDFTBuilder::new_rdft(1 << (frame_bits - 1), true, false)) | |
430 | } else { | |
431 | Transform::DCT(DCT::new(DCTMode::DCT_II, 1 << frame_bits)) | |
432 | }; | |
433 | self.scale = if !self.use_dct { | |
434 | 1.0 / (32768.0 * ((1 << frame_bits) as f32).sqrt()) | |
435 | } else { | |
436 | (2.0 / ((1 << frame_bits) as f32)).sqrt() / 1024.0 | |
437 | }; | |
438 | let s_srate = if single { srate } else { srate >> 1 } as usize; | |
439 | init_bands(s_srate, 1 << frame_bits, &mut self.num_bands, &mut self.bands); | |
440 | self.first_frame = true; | |
441 | ||
442 | self.samples.reset(); | |
443 | self.samples.frame_len = 1 << frame_bits; | |
444 | self.samples.step_size = duration * usize::from(ainfo.channels); | |
445 | self.frm_size = duration * usize::from(ainfo.channels); | |
446 | self.overlap_size = self.frm_size / 15; | |
447 | self.tmp = vec![0.0; 1 << frame_bits]; | |
448 | ||
449 | let blk_len = ((1 << frame_bits) - (1 << (frame_bits - 4))) >> if self.stereo { 1 } else { 0 }; | |
450 | self.rc.init(encinfo.quality, encinfo.bitrate, srate, blk_len as u32); | |
451 | ||
452 | let edata = vec![b'B', b'I', b'K', self.version as u8]; | |
453 | ||
454 | let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, SND_F32P_FORMAT, self.frm_size); | |
455 | let name = if !self.use_dct { "bink-audio-rdft" } else { "bink-audio-dct" }; | |
456 | let info = NACodecInfo::new(name, NACodecTypeInfo::Audio(out_ainfo), Some(edata)); | |
457 | let mut stream = NAStream::new(StreamType::Audio, stream_id, info, self.frm_size as u32, ainfo.sample_rate, 0); | |
458 | stream.set_num(stream_id as usize); | |
459 | let stream = stream.into_ref(); | |
460 | ||
461 | self.stream = Some(stream.clone()); | |
462 | self.sample_step = (encinfo.tb_num as usize) * (ainfo.sample_rate as usize) / (encinfo.tb_den as usize); | |
463 | ||
464 | Ok(stream) | |
465 | }, | |
466 | } | |
467 | } | |
468 | fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { | |
469 | if let Some(ref abuf) = frm.get_buffer().get_abuf_f32() { | |
470 | let src = abuf.get_data(); | |
471 | let length = abuf.get_length(); | |
472 | ||
473 | if abuf.get_info().get_channels() == 2 { | |
474 | let offset = abuf.get_offset(1); | |
475 | self.samples.add_stereo(&src[..length], &src[offset..][..length]); | |
476 | } else { | |
477 | self.samples.add_mono(&src[..length]); | |
478 | } | |
479 | ||
480 | while self.encode_packet(false)? { | |
481 | } | |
482 | ||
483 | Ok(()) | |
484 | } else { | |
485 | Err(EncoderError::InvalidParameters) | |
486 | } | |
487 | } | |
488 | fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> { | |
489 | Ok(self.packets.pop_front()) | |
490 | } | |
491 | fn flush(&mut self) -> EncoderResult<()> { | |
492 | if self.samples.num_avail() > 0 { | |
493 | self.encode_packet(true)?; | |
494 | } | |
495 | Ok(()) | |
496 | } | |
497 | } | |
498 | ||
499 | impl Drop for BinkAudioEncoder { | |
500 | fn drop(&mut self) { | |
501 | if self.print_stats && self.nblks > 0 { | |
502 | println!("encoded {} block(s) in {} frame(s), {} samples total ({}s)", | |
503 | self.nblks, self.nframes, self.nsamples, | |
504 | (self.nsamples as f32) / (self.rc.srate as f32)); | |
505 | ||
506 | let bitrate = (self.blk_bits as u64) * u64::from(self.rc.srate) / (self.nsamples as u64); | |
507 | let br_fmt = if bitrate >= 10_000_000 { | |
508 | format!("{}mbps", bitrate / 1000000) | |
509 | } else if bitrate >= 10_000 { | |
510 | format!("{}kbps", bitrate / 1000) | |
511 | } else { | |
512 | format!("{}bps", bitrate) | |
513 | }; | |
514 | println!("average bitrate {}", br_fmt); | |
515 | ||
516 | let mut end = self.qstats.len(); | |
517 | for (i, &cnt) in self.qstats.iter().enumerate().rev() { | |
518 | if cnt != 0 { | |
519 | break; | |
520 | } | |
521 | end = i; | |
522 | } | |
523 | print!("quants used:"); | |
524 | for &cnt in self.qstats.iter().take(end) { | |
525 | print!(" {}", cnt); | |
526 | } | |
527 | println!(); | |
528 | ||
529 | let mut end = self.bstats.len(); | |
530 | for (i, &cnt) in self.bstats.iter().enumerate().rev() { | |
531 | if cnt != 0 { | |
532 | break; | |
533 | } | |
534 | end = i; | |
535 | } | |
536 | print!("coefficient bits:"); | |
537 | for &cnt in self.bstats.iter().take(end) { | |
538 | print!(" {}", cnt); | |
539 | } | |
540 | println!(); | |
541 | } | |
542 | } | |
543 | } | |
544 | ||
545 | const ENCODER_OPTS: &[NAOptionDefinition] = &[ | |
546 | NAOptionDefinition { | |
547 | name: "version", description: "codec version", | |
548 | opt_type: NAOptionDefinitionType::String(Some(&["b", "f", "g", "h", "i", "k"])) }, | |
549 | NAOptionDefinition { | |
550 | name: "preload", description: "number of audio frames to preload", | |
551 | opt_type: NAOptionDefinitionType::Int(Some(1), Some(256)) }, | |
552 | NAOptionDefinition { | |
553 | name: "print_stats", description: "print encoder statistics", | |
554 | opt_type: NAOptionDefinitionType::Bool }, | |
555 | ]; | |
556 | ||
557 | impl NAOptionHandler for BinkAudioEncoder { | |
558 | fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS } | |
559 | fn set_options(&mut self, options: &[NAOption]) { | |
560 | for option in options.iter() { | |
561 | for opt_def in ENCODER_OPTS.iter() { | |
562 | if opt_def.check(option).is_ok() { | |
563 | match option.name { | |
564 | "version" => { | |
565 | if let NAValue::String(ref strval) = option.value { | |
566 | match strval.as_str() { | |
567 | "b" => self.version = 'b', | |
568 | _ => { | |
569 | println!("versions beside 'b' are not supported"); | |
570 | }, | |
571 | }; | |
572 | } | |
573 | }, | |
574 | "preload" => { | |
575 | if let NAValue::Int(ival) = option.value { | |
576 | self.preload = ival as usize; | |
577 | } | |
578 | }, | |
579 | "print_stats" => { | |
580 | if let NAValue::Bool(bval) = option.value { | |
581 | self.print_stats = bval; | |
582 | } | |
583 | }, | |
584 | _ => {}, | |
585 | }; | |
586 | } | |
587 | } | |
588 | } | |
589 | } | |
590 | fn query_option_value(&self, name: &str) -> Option<NAValue> { | |
591 | match name { | |
592 | "version" => Some(NAValue::String(self.version.to_string())), | |
593 | "preload" => Some(NAValue::Int(self.preload as i64)), | |
594 | "print_stats" => Some(NAValue::Bool(self.print_stats)), | |
595 | _ => None, | |
596 | } | |
597 | } | |
598 | } | |
599 | ||
600 | pub fn get_encoder_rdft() -> Box<dyn NAEncoder + Send> { Box::new(BinkAudioEncoder::new(false)) } | |
601 | #[allow(dead_code)] | |
602 | pub fn get_encoder_dct() -> Box<dyn NAEncoder + Send> { Box::new(BinkAudioEncoder::new(true)) } | |
603 | ||
604 | #[cfg(test)] | |
605 | mod test { | |
606 | use nihav_core::codecs::*; | |
607 | use nihav_core::demuxers::*; | |
608 | use nihav_core::muxers::*; | |
609 | use nihav_codec_support::test::enc_video::*; | |
610 | use crate::*; | |
611 | use nihav_commonfmt::*; | |
612 | ||
613 | fn test_encoder(name: &'static str, enc_options: &[NAOption], | |
614 | bitrate: u32, quality: u8, channels: u8, hash: &[u32; 4]) { | |
615 | let mut dmx_reg = RegisteredDemuxers::new(); | |
616 | generic_register_all_demuxers(&mut dmx_reg); | |
617 | let mut dec_reg = RegisteredDecoders::new(); | |
618 | generic_register_all_decoders(&mut dec_reg); | |
619 | let mut mux_reg = RegisteredMuxers::new(); | |
620 | rad_register_all_muxers(&mut mux_reg); | |
621 | let mut enc_reg = RegisteredEncoders::new(); | |
622 | rad_register_all_encoders(&mut enc_reg); | |
623 | ||
624 | // sample from a private collection | |
625 | let dec_config = DecoderTestParams { | |
626 | demuxer: "wav", | |
627 | in_name: "assets/test-stereo.wav", | |
628 | stream_type: StreamType::Audio, | |
629 | limit: None,//Some(20), | |
630 | dmx_reg, dec_reg, | |
631 | }; | |
632 | let enc_config = EncoderTestParams { | |
633 | muxer: "bink", | |
634 | enc_name: "bink-audio-rdft", | |
635 | out_name: name, | |
636 | mux_reg, enc_reg, | |
637 | }; | |
638 | let dst_ainfo = NAAudioInfo { | |
639 | sample_rate: 0, | |
640 | channels, | |
641 | format: SND_F32P_FORMAT, | |
642 | block_len: 1024, | |
643 | }; | |
644 | let enc_params = EncodeParameters { | |
645 | format: NACodecTypeInfo::Audio(dst_ainfo), | |
646 | quality, | |
647 | bitrate: bitrate * 1000, | |
648 | tb_num: 0, | |
649 | tb_den: 0, | |
650 | flags: 0, | |
651 | }; | |
652 | //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); | |
653 | ||
654 | test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, hash); | |
655 | } | |
656 | #[test] | |
657 | fn test_binkaud_encoder_b_q100() { | |
658 | let enc_options = &[ | |
659 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
660 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
661 | ]; | |
662 | test_encoder("bink-aud-b-q80.bik", enc_options, 0, 100, 2, | |
663 | &[0x45c13e44, 0x36ab1efb, 0x84c93f1a, 0x4aa49831]); | |
664 | } | |
665 | #[test] | |
666 | fn test_binkaud_encoder_b_q80() { | |
667 | let enc_options = &[ | |
668 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
669 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
670 | ]; | |
671 | test_encoder("bink-aud-b-q80.bik", enc_options, 0, 80, 2, | |
672 | &[0x3835b6ff, 0xd2da247b, 0xae8ff168, 0x464b4c31]); | |
673 | } | |
674 | #[test] | |
675 | fn test_binkaud_encoder_b_q40() { | |
676 | let enc_options = &[ | |
677 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
678 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
679 | ]; | |
680 | test_encoder("bink-aud-b-q80.bik", enc_options, 0, 40, 2, | |
681 | &[0xe99882b0, 0x12f9be7c, 0xc634c1a7, 0xd88e1c9b]); | |
682 | } | |
683 | #[test] | |
684 | fn test_binkaud_encoder_b_q1() { | |
685 | let enc_options = &[ | |
686 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
687 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
688 | ]; | |
689 | test_encoder("bink-aud-b-q80.bik", enc_options, 0, 1, 2, | |
690 | &[0x783d68e1, 0x9db89348, 0x5348e677, 0x337133fa]); | |
691 | } | |
692 | #[test] | |
693 | fn test_binkaud_encoder_b_br240() { | |
694 | let enc_options = &[ | |
695 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
696 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
697 | ]; | |
698 | test_encoder("bink-aud-b-180k.bik", enc_options, 240, 0, 2, | |
699 | &[0xad33939e, 0x945413f1, 0xf5edc6be, 0xcf8eebd3]); | |
700 | } | |
701 | #[test] | |
702 | fn test_binkaud_encoder_b_br180() { | |
703 | let enc_options = &[ | |
704 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
705 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
706 | ]; | |
707 | test_encoder("bink-aud-b-180k.bik", enc_options, 180, 0, 2, | |
708 | &[0x4d33f1be, 0xb1e662ad, 0x71bd0486, 0x327e053a]); | |
709 | } | |
710 | #[test] | |
711 | fn test_binkaud_encoder_b_br120() { | |
712 | let enc_options = &[ | |
713 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
714 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
715 | ]; | |
716 | test_encoder("bink-aud-b-120k.bik", enc_options, 120, 0, 2, | |
717 | &[0xa1e17945, 0xd837677a, 0x48cd0b3a, 0x3e7c1a03]); | |
718 | } | |
719 | #[test] | |
720 | fn test_binkaud_encoder_b_br60() { | |
721 | let enc_options = &[ | |
722 | NAOption{name: "version", value: NAValue::String("b".to_owned())}, | |
723 | //NAOption{name: "print_stats", value: NAValue::Bool(true)}, | |
724 | ]; | |
725 | test_encoder("bink-aud-b-60k.bik", enc_options, 60, 0, 2, | |
726 | &[0xf48cae2e, 0x038ec363, 0x17cb1606, 0x4756f854]); | |
727 | } | |
728 | } |