]>
Commit | Line | Data |
---|---|---|
1 | use nihav_core::codecs::*; | |
2 | use nihav_core::io::byteio::*; | |
3 | use nihav_core::io::bitwriter::*; | |
4 | use nihav_codec_support::dsp::fft::*; | |
5 | use super::cookdata::*; | |
6 | ||
7 | /* | |
8 | TODO: | |
9 | - calculate gains? | |
10 | */ | |
11 | const MAX_FRAME_SIZE: usize = 1024; | |
12 | ||
13 | const EPS: f32 = 1.0e-6; | |
14 | ||
15 | fn get_block_size(srate: u32) -> usize { | |
16 | match srate { | |
17 | 8000 | 11025 => 256, | |
18 | 22050 => 512, | |
19 | _ => 1024, | |
20 | } | |
21 | } | |
22 | ||
23 | trait ClipCat { | |
24 | fn clip_cat(&self) -> usize; | |
25 | } | |
26 | ||
27 | impl ClipCat for i32 { | |
28 | fn clip_cat(&self) -> usize { ((*self).max(0) as usize).min(NUM_CATEGORIES - 1) } | |
29 | } | |
30 | ||
31 | fn bitalloc(samples: usize, bits: usize, vector_bits: u8, total_subbands: usize, qindex: &[i8], category: &mut [u8], cat_index: &mut [u8]) { | |
32 | let avail_bits = (if bits > samples { samples + ((bits - samples) * 5) / 8 } else { bits }) as i32; | |
33 | ||
34 | let mut bias: i32 = -32; | |
35 | for i in 0..6 { | |
36 | let mut sum = 0; | |
37 | for j in 0..total_subbands { | |
38 | let idx = ((32 >> i) + bias - (qindex[j] as i32)) / 2; | |
39 | sum += COOK_EXP_BITS[idx.clip_cat()]; | |
40 | } | |
41 | if sum >= (avail_bits - 32) { | |
42 | bias += 32 >> i; | |
43 | } | |
44 | } | |
45 | ||
46 | let mut exp_index1: [usize; MAX_SUBBANDS * 2] = [0; MAX_SUBBANDS * 2]; | |
47 | let mut exp_index2: [usize; MAX_SUBBANDS * 2] = [0; MAX_SUBBANDS * 2]; | |
48 | let mut sum = 0; | |
49 | for i in 0..total_subbands { | |
50 | let idx = ((bias - (qindex[i] as i32)) / 2).clip_cat(); | |
51 | sum += COOK_EXP_BITS[idx]; | |
52 | exp_index1[i] = idx; | |
53 | exp_index2[i] = idx; | |
54 | } | |
55 | ||
56 | let mut tbias1 = sum; | |
57 | let mut tbias2 = sum; | |
58 | let mut tcat: [usize; 128*2] = [0; 128*2]; | |
59 | let mut tcat_idx1 = 128; | |
60 | let mut tcat_idx2 = 128; | |
61 | for _ in 1..(1 << vector_bits) { | |
62 | if tbias1 + tbias2 > avail_bits * 2 { | |
63 | let mut max = -999999; | |
64 | let mut idx = total_subbands + 1; | |
65 | for j in 0..total_subbands { | |
66 | if exp_index1[j] >= (NUM_CATEGORIES - 1) { continue; } | |
67 | let t = -2 * (exp_index1[j] as i32) - (qindex[j] as i32) + bias; | |
68 | if t >= max { | |
69 | max = t; | |
70 | idx = j; | |
71 | } | |
72 | } | |
73 | if idx >= total_subbands { break; } | |
74 | tcat[tcat_idx1] = idx; | |
75 | tcat_idx1 += 1; | |
76 | tbias1 -= COOK_EXP_BITS[exp_index1[idx]] - COOK_EXP_BITS[exp_index1[idx] + 1]; | |
77 | exp_index1[idx] += 1; | |
78 | } else { | |
79 | let mut min = 999999; | |
80 | let mut idx = total_subbands + 1; | |
81 | for j in 0..total_subbands { | |
82 | if exp_index2[j] == 0 { continue; } | |
83 | let t = -2 * (exp_index2[j] as i32) - (qindex[j] as i32) + bias; | |
84 | if t < min { | |
85 | min = t; | |
86 | idx = j; | |
87 | } | |
88 | } | |
89 | if idx >= total_subbands { break; } | |
90 | tcat_idx2 -= 1; | |
91 | tcat[tcat_idx2] = idx; | |
92 | tbias2 -= COOK_EXP_BITS[exp_index2[idx]] - COOK_EXP_BITS[exp_index2[idx] - 1]; | |
93 | exp_index2[idx] -= 1; | |
94 | } | |
95 | } | |
96 | for i in 0..total_subbands { | |
97 | category[i] = exp_index2[i] as u8; | |
98 | } | |
99 | for el in cat_index.iter_mut() { | |
100 | *el = 255; | |
101 | } | |
102 | for (dst, &src) in cat_index.iter_mut().zip(tcat[tcat_idx2..tcat_idx1].iter()) { | |
103 | *dst = src as u8; | |
104 | } | |
105 | } | |
106 | ||
107 | fn map_coef(val: f32, centroids: &[f32]) -> usize { | |
108 | if val < centroids[1] * 0.5 { | |
109 | 0 | |
110 | } else { | |
111 | let len = centroids.len(); | |
112 | if val < centroids[len - 1] { | |
113 | for (i, pair) in centroids.windows(2).enumerate().skip(1) { | |
114 | if val <= (pair[0] + pair[1]) * 0.5 { | |
115 | return i; | |
116 | } | |
117 | } | |
118 | } | |
119 | len - 1 | |
120 | } | |
121 | } | |
122 | ||
123 | fn couple_bands(left: &[f32], right: &[f32], dst: &mut [f32], cpl_scales: &[f32]) -> u8 { | |
124 | let nrg0 = left.iter().fold(0.0f32, |acc, &v| acc + v * v); | |
125 | let nrg1 = right.iter().fold(0.0f32, |acc, &v| acc + v * v); | |
126 | let last_idx = cpl_scales.len() - 3; | |
127 | match (nrg0 > EPS, nrg1 > EPS) { | |
128 | (true, true) => { | |
129 | let tgt_scale0 = (nrg0 / (nrg0 + nrg1)).sqrt(); | |
130 | let tgt_scale1 = (nrg1 / (nrg0 + nrg1)).sqrt(); | |
131 | ||
132 | let mut best_dist = 42.0; | |
133 | let mut best_idx = 0; | |
134 | for i in 0..=last_idx { | |
135 | let scale0 = cpl_scales[i]; | |
136 | let scale1 = cpl_scales[cpl_scales.len() - 1 - i]; | |
137 | let dist = (scale0 - tgt_scale0).abs() + (scale1 - tgt_scale1).abs(); | |
138 | if dist < best_dist { | |
139 | best_dist = dist; | |
140 | best_idx = i; | |
141 | } | |
142 | } | |
143 | let scale_l = cpl_scales[best_idx]; | |
144 | let scale_r = cpl_scales[cpl_scales.len() - 1 - best_idx]; | |
145 | ||
146 | if best_idx == 0 { | |
147 | dst.copy_from_slice(left); | |
148 | } else { | |
149 | for (dst, (&ll, &rr)) in dst.iter_mut().zip(left.iter().zip(right.iter())) { | |
150 | *dst = (ll / scale_l + rr / scale_r) * 0.5; | |
151 | } | |
152 | } | |
153 | best_idx as u8 | |
154 | }, | |
155 | (false, true) => { | |
156 | dst.copy_from_slice(right); | |
157 | last_idx as u8 | |
158 | }, | |
159 | (true, false) => { | |
160 | dst.copy_from_slice(left); | |
161 | 0 | |
162 | }, | |
163 | _ => { | |
164 | for (dst, (&ll, &rr)) in dst.iter_mut().zip(left.iter().zip(right.iter())) { | |
165 | *dst = (ll + rr) * std::f32::consts::FRAC_1_SQRT_2; | |
166 | } | |
167 | (cpl_scales.len() / 2) as u8 | |
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | #[derive(Clone,Copy,Default)] | |
173 | struct PackedCoeffs { | |
174 | cw: [u16; 10], | |
175 | bits: [u8; 10], | |
176 | signs: [u16; 10], | |
177 | nnz: [u8; 10], | |
178 | num: usize, | |
179 | cat: usize, | |
180 | nbits: u16, | |
181 | } | |
182 | ||
183 | impl PackedCoeffs { | |
184 | fn pack(&mut self, coeffs: &[f32], cat: usize) -> u16 { | |
185 | self.cat = cat; | |
186 | self.nbits = 0; | |
187 | ||
188 | if cat >= COOK_NUM_VQ_GROUPS.len() { | |
189 | self.num = 0; | |
190 | return 0; | |
191 | } | |
192 | ||
193 | let group_size = COOK_VQ_GROUP_SIZE[cat]; | |
194 | let multiplier = COOK_VQ_MULT[cat] as usize + 1; | |
195 | let (code_words, code_bits) = match cat { | |
196 | 0 => (&COOK_VQ0_CODES[..], &COOK_VQ0_BITS[..]), | |
197 | 1 => (&COOK_VQ1_CODES[..], &COOK_VQ1_BITS[..]), | |
198 | 2 => (&COOK_VQ2_CODES[..], &COOK_VQ2_BITS[..]), | |
199 | 3 => (&COOK_VQ3_CODES[..], &COOK_VQ3_BITS[..]), | |
200 | 4 => (&COOK_VQ4_CODES[..], &COOK_VQ4_BITS[..]), | |
201 | 5 => (&COOK_VQ5_CODES[..], &COOK_VQ5_BITS[..]), | |
202 | 6 => (&COOK_VQ6_CODES[..], &COOK_VQ6_BITS[..]), | |
203 | _ => unreachable!(), | |
204 | }; | |
205 | let centroids = &COOK_QUANT_CENTROID[cat][..multiplier]; | |
206 | self.num = COOK_NUM_VQ_GROUPS[cat]; | |
207 | ||
208 | for (group_no, group) in coeffs.chunks(group_size).enumerate() { | |
209 | let mut cw = 0; | |
210 | let mut cvals = [0; 5]; | |
211 | let mut sarr = [0; 5]; | |
212 | for ((dval, sign), &el) in cvals.iter_mut().zip(sarr.iter_mut()).zip(group.iter()) { | |
213 | let cur_val = map_coef(el.abs(), centroids); | |
214 | *dval = cur_val; | |
215 | *sign = (el < 0.0) as u16; | |
216 | cw = cw * multiplier + cur_val; | |
217 | } | |
218 | while cw >= code_bits.len() || code_bits[cw] == 0 { | |
219 | let mut max_pos = 0; | |
220 | let mut max_val = cvals[0]; | |
221 | for (i, &val) in cvals.iter().enumerate().skip(1) { | |
222 | if val > max_val { | |
223 | max_val = val; | |
224 | max_pos = i; | |
225 | } | |
226 | } | |
227 | cvals[max_pos] -= 1; | |
228 | cw = 0; | |
229 | for &dval in cvals.iter().take(group_size) { | |
230 | cw = cw * multiplier + dval; | |
231 | } | |
232 | } | |
233 | let mut signs = 0; | |
234 | let mut nnz = 0u8; | |
235 | for (&sign, &val) in sarr.iter().zip(cvals.iter()) { | |
236 | if val != 0 { | |
237 | signs = (signs << 1) | sign; | |
238 | nnz += 1; | |
239 | } | |
240 | } | |
241 | ||
242 | self.cw [group_no] = code_words[cw]; | |
243 | self.bits [group_no] = code_bits[cw]; | |
244 | self.signs[group_no] = signs; | |
245 | self.nnz [group_no] = nnz; | |
246 | self.nbits += u16::from(code_bits[cw]) + u16::from(nnz); | |
247 | } | |
248 | self.nbits | |
249 | } | |
250 | fn write(&self, bw: &mut BitWriter, mut bits_left: u16) { | |
251 | for ((&cw, &bits), (&signs, &nnz)) in | |
252 | self.cw.iter().zip(self.bits.iter()).zip( | |
253 | self.signs.iter().zip(self.nnz.iter())).take(self.num) { | |
254 | let cur_bits = u16::from(bits + nnz); | |
255 | if cur_bits > bits_left { | |
256 | break; | |
257 | } | |
258 | bits_left -= cur_bits; | |
259 | bw.write(cw.into(), bits); | |
260 | if nnz > 0 { | |
261 | bw.write(signs.into(), nnz); | |
262 | } | |
263 | } | |
264 | } | |
265 | } | |
266 | ||
267 | struct TempData { | |
268 | bands: [PackedCoeffs; MAX_SUBBANDS * 2], | |
269 | } | |
270 | ||
271 | impl Default for TempData { | |
272 | fn default() -> Self { | |
273 | Self { | |
274 | bands: [PackedCoeffs::default(); MAX_SUBBANDS * 2], | |
275 | } | |
276 | } | |
277 | } | |
278 | ||
279 | struct ChannelDataParams<'a> { | |
280 | size: usize, | |
281 | frame_size: usize, | |
282 | hpow_tab: &'a [f32; 128], | |
283 | coupling: &'a [u8], | |
284 | js_bits: u8, | |
285 | js_start: usize, | |
286 | vector_bits: u8, | |
287 | } | |
288 | ||
289 | struct CookChannelPair { | |
290 | br_info: &'static BitrateParams, | |
291 | delay: [[f32; MAX_FRAME_SIZE]; 2], | |
292 | } | |
293 | ||
294 | fn calc_qindex(nrg: f32) -> i8 { | |
295 | let mut nrg0 = nrg * 0.05; | |
296 | if nrg0 <= 1.0 { | |
297 | nrg0 *= std::f32::consts::FRAC_1_SQRT_2; | |
298 | } else { | |
299 | nrg0 *= std::f32::consts::SQRT_2; | |
300 | } | |
301 | nrg0.log2().max(-31.0).min(47.0) as i8 | |
302 | } | |
303 | ||
304 | impl CookChannelPair { | |
305 | fn new(br_info: &'static BitrateParams) -> Self { | |
306 | Self { | |
307 | br_info, | |
308 | delay: [[0.0; MAX_FRAME_SIZE]; 2], | |
309 | } | |
310 | } | |
311 | fn encode_bands(params: ChannelDataParams, dbuf: Vec<u8>, coeffs: &mut [f32], total_bands: usize, tmp: &mut TempData) -> EncoderResult<Vec<u8>> { | |
312 | let output_start = dbuf.len(); | |
313 | let mut bw = BitWriter::new(dbuf, BitWriterMode::BE); | |
314 | let data_end = bw.tell() + params.frame_size; | |
315 | ||
316 | let mut qindex = [0i8; MAX_SUBBANDS * 2]; | |
317 | for (qscale, band) in qindex.iter_mut().zip(coeffs.chunks(BAND_SIZE)).take(total_bands) { | |
318 | let nrg = band.iter().fold(0.0f32, |acc, &v| acc + v * v); | |
319 | *qscale = calc_qindex(nrg); | |
320 | } | |
321 | qindex[0] = qindex[0].max(-6); | |
322 | ||
323 | bw.write0(); // no gains | |
324 | //todo gains | |
325 | ||
326 | if params.js_bits > 0 { | |
327 | let (cpl_cb_codes, cpl_cb_bits) = match params.js_bits { | |
328 | 2 => (&COOK_CPL_2BITS_CODES[..], &COOK_CPL_2BITS_BITS[..]), | |
329 | 3 => (&COOK_CPL_3BITS_CODES[..], &COOK_CPL_3BITS_BITS[..]), | |
330 | 4 => (&COOK_CPL_4BITS_CODES[..], &COOK_CPL_4BITS_BITS[..]), | |
331 | 5 => (&COOK_CPL_5BITS_CODES[..], &COOK_CPL_5BITS_BITS[..]), | |
332 | 6 => (&COOK_CPL_6BITS_CODES[..], &COOK_CPL_6BITS_BITS[..]), | |
333 | _ => unreachable!(), | |
334 | }; | |
335 | let mut bit_size = 0; | |
336 | let mut raw_bit_size = 0; | |
337 | for &el in params.coupling.iter() { | |
338 | bit_size += cpl_cb_bits[usize::from(el)]; | |
339 | raw_bit_size += params.js_bits; | |
340 | } | |
341 | if bit_size < raw_bit_size { | |
342 | bw.write1(); | |
343 | for &el in params.coupling.iter() { | |
344 | let idx = usize::from(el); | |
345 | bw.write(cpl_cb_codes[idx].into(), cpl_cb_bits[idx]); | |
346 | } | |
347 | } else { | |
348 | bw.write0(); | |
349 | for &el in params.coupling.iter() { | |
350 | bw.write(u32::from(el), params.js_bits); | |
351 | } | |
352 | } | |
353 | } | |
354 | ||
355 | let mut last_q = qindex[0]; | |
356 | bw.write((qindex[0] + 6) as u32, 6); | |
357 | if params.js_bits == 0 { | |
358 | for (i, qscale) in qindex[..total_bands].iter_mut().enumerate().skip(1) { | |
359 | let cb_idx = (i - 1).min(12); | |
360 | let diff = (*qscale - last_q).max(-12).min(11); | |
361 | *qscale = last_q + diff; | |
362 | last_q = *qscale; | |
363 | ||
364 | let idx2 = (diff + 12) as usize; | |
365 | bw.write(COOK_QUANT_CODES[cb_idx][idx2].into(), COOK_QUANT_BITS[cb_idx][idx2]); | |
366 | } | |
367 | } else { | |
368 | for (i, qscale) in qindex[..total_bands].iter_mut().enumerate().skip(1) { | |
369 | let band_no = if i < params.js_start * 2 { i >> 1 } else { i - params.js_start }; | |
370 | let cb_idx = band_no.saturating_sub(1).min(12); | |
371 | let diff = (*qscale - last_q).max(-12).min(11); | |
372 | *qscale = last_q + diff; | |
373 | last_q = *qscale; | |
374 | ||
375 | let idx2 = (diff + 12) as usize; | |
376 | bw.write(COOK_QUANT_CODES[cb_idx][idx2].into(), COOK_QUANT_BITS[cb_idx][idx2]); | |
377 | } | |
378 | } | |
379 | ||
380 | let mut category = [0; MAX_SUBBANDS * 2]; | |
381 | let mut cat_index = [0; 127]; | |
382 | let bits_avail = data_end - bw.tell() - usize::from(params.vector_bits); | |
383 | ||
384 | bitalloc(params.size, bits_avail, params.vector_bits, total_bands, &qindex, &mut category, &mut cat_index); | |
385 | ||
386 | let mut tot_bits = 0; | |
387 | for ((band, packed), (&qindex, &cat)) in | |
388 | coeffs.chunks_exact_mut(BAND_SIZE).zip(tmp.bands.iter_mut()) | |
389 | .zip(qindex.iter().zip(category.iter())).take(total_bands) { | |
390 | for coef in band.iter_mut() { | |
391 | *coef *= params.hpow_tab[(64 - qindex) as usize]; | |
392 | } | |
393 | tot_bits += packed.pack(band, cat.into()); | |
394 | } | |
395 | ||
396 | let mut bits_left = bits_avail as u16; | |
397 | let mut bands_corrected = 0; | |
398 | let max_corr_bands = (1 << params.vector_bits) - 1; | |
399 | for &index in cat_index.iter() { | |
400 | if bands_corrected >= max_corr_bands || tot_bits <= bits_left || index == 255 { | |
401 | break; | |
402 | } | |
403 | let index = usize::from(index); | |
404 | let pband = &mut tmp.bands[index]; | |
405 | let band_coeffs = &coeffs[index * BAND_SIZE..][..BAND_SIZE]; | |
406 | let new_cat = (pband.cat + 1).min(NUM_CATEGORIES - 1); | |
407 | tot_bits -= pband.nbits; | |
408 | pband.pack(band_coeffs, new_cat); | |
409 | tot_bits += pband.nbits; | |
410 | bands_corrected += 1; | |
411 | } | |
412 | ||
413 | bw.write(bands_corrected, params.vector_bits); | |
414 | for packed in tmp.bands.iter().take(total_bands) { | |
415 | packed.write(&mut bw, bits_left); | |
416 | bits_left = bits_left.saturating_sub(packed.nbits); | |
417 | if bits_left == 0 { | |
418 | break; | |
419 | } | |
420 | } | |
421 | ||
422 | pad(&mut bw, data_end); | |
423 | ||
424 | let mut dbuf = bw.end(); | |
425 | for (i, el) in dbuf[output_start..].iter_mut().enumerate() { | |
426 | *el ^= COOK_XOR_KEY[i & 3]; | |
427 | } | |
428 | ||
429 | Ok(dbuf) | |
430 | } | |
431 | fn encode_mono(&mut self, dbuf: Vec<u8>, dsp: &mut CookDSP, src: &[f32], ch_no: usize, tmp: &mut TempData) -> EncoderResult<Vec<u8>> { | |
432 | dsp.mdct(&mut self.delay[ch_no], src, true); | |
433 | let coeffs = &mut dsp.coeffs; | |
434 | ||
435 | let frame_size = (self.br_info.frame_bits / u32::from(self.br_info.channels)) as usize; | |
436 | let total_bands: usize = self.br_info.max_subbands.into(); | |
437 | ||
438 | let params = ChannelDataParams { | |
439 | size: dsp.size, | |
440 | hpow_tab: &dsp.hpow_tab, | |
441 | coupling: &[], | |
442 | js_bits: 0, | |
443 | js_start: 0, | |
444 | vector_bits: 5, | |
445 | frame_size, | |
446 | }; | |
447 | Self::encode_bands(params, dbuf, coeffs, total_bands, tmp) | |
448 | } | |
449 | fn encode_jstereo(&mut self, dbuf: Vec<u8>, dsp: &mut CookDSP, l_ch: &[f32], r_ch: &[f32], tmp: &mut TempData) -> EncoderResult<Vec<u8>> { | |
450 | let frame_size = self.br_info.frame_bits as usize; | |
451 | let low_bands = usize::from(self.br_info.js_start); | |
452 | let total_bands = usize::from(self.br_info.max_subbands) + low_bands; | |
453 | ||
454 | dsp.mdct(&mut self.delay[0], l_ch, true); | |
455 | dsp.mdct(&mut self.delay[1], r_ch, false); | |
456 | ||
457 | let mut decouple = [0; 20]; | |
458 | let cpl_scales = COOK_CPL_SCALES[usize::from(self.br_info.js_bits - 2)]; | |
459 | let cpl_start_band = COOK_CPL_BAND[usize::from(self.br_info.js_start)] as usize; | |
460 | let cpl_end_band = COOK_CPL_BAND[usize::from(self.br_info.max_subbands) - 1] as usize; | |
461 | ||
462 | let coeffs = &mut dsp.coeffs; | |
463 | let coeffs2 = &mut dsp.coeffs2; | |
464 | let coupled = &mut dsp.coupled; | |
465 | for (dst, (src0, src1)) in coupled.chunks_exact_mut(BAND_SIZE * 2).zip(coeffs.chunks(BAND_SIZE).zip(coeffs2.chunks(BAND_SIZE))).take(low_bands) { | |
466 | let (dst0, dst1) = dst.split_at_mut(BAND_SIZE); | |
467 | dst0.copy_from_slice(src0); | |
468 | dst1.copy_from_slice(src1); | |
469 | } | |
470 | for el in coupled[total_bands * BAND_SIZE..].iter_mut() { | |
471 | *el = 0.0; | |
472 | } | |
473 | ||
474 | let mut band = low_bands; | |
475 | let mut start_band = band; | |
476 | let mut cpl_band = cpl_start_band; | |
477 | let mut last_cpl_band = cpl_band; | |
478 | let end_band = usize::from(self.br_info.max_subbands); | |
479 | while band < end_band { | |
480 | cpl_band = usize::from(COOK_CPL_BAND[band]); | |
481 | if cpl_band != last_cpl_band { | |
482 | let length = (band - start_band) * BAND_SIZE; | |
483 | decouple[last_cpl_band] = couple_bands(&coeffs[start_band * BAND_SIZE..][..length], | |
484 | &coeffs2[start_band * BAND_SIZE..][..length], | |
485 | &mut coupled[(start_band + low_bands) * BAND_SIZE..][..length], | |
486 | cpl_scales); | |
487 | last_cpl_band = cpl_band; | |
488 | start_band = band; | |
489 | } | |
490 | band += 1; | |
491 | } | |
492 | if band != start_band { | |
493 | let length = (band - start_band) * BAND_SIZE; | |
494 | decouple[last_cpl_band] = couple_bands(&coeffs[start_band * BAND_SIZE..][..length], | |
495 | &coeffs2[start_band * BAND_SIZE..][..length], | |
496 | &mut coupled[(start_band + low_bands) * BAND_SIZE..][..length], | |
497 | cpl_scales); | |
498 | } | |
499 | ||
500 | let vector_bits = match dsp.size { | |
501 | 1024 => 7, | |
502 | 512 => 6, | |
503 | _ => 5, | |
504 | }; | |
505 | let params = ChannelDataParams { | |
506 | size: dsp.size, | |
507 | hpow_tab: &dsp.hpow_tab, | |
508 | coupling: &decouple[cpl_start_band..=cpl_end_band], | |
509 | js_bits: self.br_info.js_bits, | |
510 | js_start: self.br_info.js_start.into(), | |
511 | frame_size, | |
512 | vector_bits, | |
513 | }; | |
514 | ||
515 | Self::encode_bands(params, dbuf, coupled, total_bands, tmp) | |
516 | } | |
517 | } | |
518 | ||
519 | fn pad(bw: &mut BitWriter, data_end: usize) { | |
520 | while (bw.tell() & 7) != 0 { | |
521 | bw.write0(); | |
522 | } | |
523 | while bw.tell() + 32 <= data_end { | |
524 | bw.write(0, 32); | |
525 | } | |
526 | while bw.tell() + 8 <= data_end { | |
527 | bw.write(0, 8); | |
528 | } | |
529 | } | |
530 | pub struct MDCT { | |
531 | table: Vec<FFTComplex>, | |
532 | tmp: Vec<FFTComplex>, | |
533 | fft: FFT, | |
534 | size: usize, | |
535 | } | |
536 | ||
537 | impl MDCT { | |
538 | pub fn new(size: usize) -> Self { | |
539 | let fft = FFTBuilder::new_fft(size / 4, false); | |
540 | let mut table = Vec::with_capacity(size / 4); | |
541 | let factor = std::f32::consts::PI * 2.0 / (size as f32); | |
542 | for i in 0..(size / 4) { | |
543 | let val = factor * ((i as f32) + 1.0 / 8.0); | |
544 | table.push(FFTComplex::exp(val)); | |
545 | } | |
546 | Self { | |
547 | fft, | |
548 | tmp: vec![FFTC_ZERO; size / 4], | |
549 | table, | |
550 | size, | |
551 | } | |
552 | } | |
553 | pub fn mdct(&mut self, src: &[f32], dst: &mut [f32]) { | |
554 | let size1_8 = self.size / 8; | |
555 | let size1_4 = self.size / 4; | |
556 | let size1_2 = self.size / 2; | |
557 | let size3_4 = size1_4 * 3; | |
558 | ||
559 | for i in 0..size1_8 { | |
560 | let a0 = FFTComplex{re: -src[2 * i + size3_4] - src[size3_4 - 1 - 2 * i], | |
561 | im: -src[size1_4 + 2 * i] + src[size1_4 - 1 - 2 * i]}; | |
562 | let t0 = !self.table[i]; | |
563 | let a1 = FFTComplex{re: src[2 * i] - src[size1_2 - 1 - 2 * i], | |
564 | im: -src[size1_2 + 2 * i] - src[self.size - 1 - 2 * i]}; | |
565 | let t1 = !self.table[size1_8 + i]; | |
566 | self.tmp[i] = a0 * t0; | |
567 | self.tmp[i + size1_8] = a1 * t1; | |
568 | } | |
569 | self.fft.do_fft_inplace(&mut self.tmp); | |
570 | ||
571 | for i in 0..size1_8 { | |
572 | let a0 = self.tmp[size1_8 - 1 - i] * !self.table[size1_8 - 1 - i].rotate(); | |
573 | let a1 = self.tmp[size1_8 + i] * !self.table[size1_8 + i].rotate(); | |
574 | dst[size1_4 - 2 - i * 2] = a0.im; | |
575 | dst[size1_4 - 1 - i * 2] = a1.re; | |
576 | dst[size1_4 + i * 2] = a1.im; | |
577 | dst[size1_4 + 1 + i * 2] = a0.re; | |
578 | } | |
579 | } | |
580 | } | |
581 | ||
582 | ||
583 | struct CookDSP { | |
584 | size: usize, | |
585 | mdct: MDCT, | |
586 | tmp: [f32; MAX_FRAME_SIZE * 2], | |
587 | window: [f32; MAX_FRAME_SIZE * 2], | |
588 | pow_tab: [f32; 128], | |
589 | hpow_tab: [f32; 128], | |
590 | gain_tab: [f32; 23], | |
591 | ||
592 | coeffs: [f32; MAX_FRAME_SIZE], | |
593 | coeffs2: [f32; MAX_FRAME_SIZE], | |
594 | coupled: [f32; MAX_FRAME_SIZE * 2], | |
595 | } | |
596 | ||
597 | impl CookDSP { | |
598 | fn init(&mut self, frame_size: usize) { | |
599 | if self.size == frame_size { | |
600 | return; | |
601 | } | |
602 | self.size = frame_size; | |
603 | self.mdct = MDCT::new(self.size * 2); | |
604 | ||
605 | let fsamples = self.size as f32; | |
606 | let factor = std::f32::consts::PI / (2.0 * fsamples); | |
607 | let scale = fsamples; | |
608 | for (k, dst) in self.window[..self.size * 2].iter_mut().enumerate() { | |
609 | *dst = (factor * ((k as f32) + 0.5)).sin() * scale; | |
610 | } | |
611 | ||
612 | for (dst, &pow_val) in self.gain_tab.iter_mut().zip(self.pow_tab[53..].iter()) { | |
613 | *dst = pow_val.powf(8.0 / fsamples); | |
614 | } | |
615 | } | |
616 | fn mdct(&mut self, delay: &mut [f32], src: &[f32], mono: bool) { | |
617 | let (w0, w1) = self.window[..self.size * 2].split_at(self.size); | |
618 | let (d0, d1) = self.tmp[..self.size * 2].split_at_mut(self.size); | |
619 | ||
620 | for (dst, (&src, &win)) in d1.iter_mut().zip(delay.iter().zip(w0.iter()).rev()) { | |
621 | *dst = src * win; | |
622 | } | |
623 | for (dst, (&src, &win)) in d0.iter_mut().zip(src.iter().zip(w1.iter()).rev()) { | |
624 | *dst = src * win; | |
625 | } | |
626 | self.mdct.mdct(&self.tmp, if mono { &mut self.coeffs } else { &mut self.coeffs2 }); | |
627 | ||
628 | delay[..self.size].copy_from_slice(&src[..self.size]); | |
629 | } | |
630 | } | |
631 | ||
632 | impl Default for CookDSP { | |
633 | fn default() -> Self { | |
634 | let mut pow_tab: [f32; 128] = [0.0; 128]; | |
635 | let mut hpow_tab: [f32; 128] = [0.0; 128]; | |
636 | for i in 0..128 { | |
637 | pow_tab[i] = 2.0f32.powf((i as f32) - 64.0); | |
638 | hpow_tab[i] = 2.0f32.powf(((i as f32) - 64.0) * 0.5); | |
639 | } | |
640 | Self { | |
641 | size: 0, | |
642 | mdct: MDCT::new(64), | |
643 | tmp: [0.0; MAX_FRAME_SIZE * 2], | |
644 | window: [0.0; MAX_FRAME_SIZE * 2], | |
645 | gain_tab: [0.0; 23], | |
646 | pow_tab, hpow_tab, | |
647 | ||
648 | coeffs: [0.0; MAX_FRAME_SIZE], | |
649 | coeffs2: [0.0; MAX_FRAME_SIZE], | |
650 | coupled: [0.0; MAX_FRAME_SIZE * 2], | |
651 | } | |
652 | } | |
653 | } | |
654 | ||
655 | #[derive(Default)] | |
656 | struct CookEncoder { | |
657 | stream: Option<NAStreamRef>, | |
658 | flavour: usize, | |
659 | force_flv: bool, | |
660 | g8_only: bool, | |
661 | ||
662 | samples: Vec<Vec<f32>>, | |
663 | tgt_size: usize, | |
664 | nframes: usize, | |
665 | frm_per_blk: usize, | |
666 | frm_size: usize, | |
667 | ||
668 | rd_pos: usize, | |
669 | wr_pos: usize, | |
670 | apts: u64, | |
671 | ||
672 | dsp: CookDSP, | |
673 | pairs: Vec<CookChannelPair>, | |
674 | tmp: TempData, | |
675 | } | |
676 | ||
677 | impl CookEncoder { | |
678 | fn new() -> Self { Self::default() } | |
679 | fn encode_packet(&mut self) -> EncoderResult<NAPacket> { | |
680 | if self.rd_pos == self.nframes { | |
681 | let mut start = self.wr_pos * self.frm_size; | |
682 | let mut dbuf = Vec::with_capacity(self.tgt_size * self.frm_per_blk); | |
683 | for _ in 0..self.frm_per_blk { | |
684 | let mut channels = self.samples.iter(); | |
685 | ||
686 | for ch_pair in self.pairs.iter_mut() { | |
687 | if ch_pair.br_info.channels == 1 { | |
688 | let ch = channels.next().unwrap(); | |
689 | dbuf = ch_pair.encode_mono(dbuf, &mut self.dsp, &ch[start..][..self.frm_size], 0, &mut self.tmp)?; | |
690 | } else { | |
691 | let l_ch = channels.next().unwrap(); | |
692 | let r_ch = channels.next().unwrap(); | |
693 | if ch_pair.br_info.js_bits == 0 { | |
694 | dbuf = ch_pair.encode_mono(dbuf, &mut self.dsp, &l_ch[start..][..self.frm_size], 0, &mut self.tmp)?; | |
695 | dbuf = ch_pair.encode_mono(dbuf, &mut self.dsp, &r_ch[start..][..self.frm_size], 1, &mut self.tmp)?; | |
696 | } else { | |
697 | dbuf = ch_pair.encode_jstereo(dbuf, &mut self.dsp, &l_ch[start..][..self.frm_size], &r_ch[start..][..self.frm_size], &mut self.tmp)?; | |
698 | } | |
699 | } | |
700 | } | |
701 | ||
702 | start += self.frm_size; | |
703 | } | |
704 | ||
705 | let first = self.wr_pos == 0; | |
706 | ||
707 | self.wr_pos += self.frm_per_blk; | |
708 | if self.wr_pos == self.nframes { | |
709 | self.wr_pos = 0; | |
710 | self.rd_pos = 0; | |
711 | } | |
712 | ||
713 | let stream = self.stream.clone().unwrap(); | |
714 | let (tb_num, tb_den) = stream.get_timebase(); | |
715 | let ts = NATimeInfo::new(Some(self.apts), None, Some(1), tb_num, tb_den); | |
716 | self.apts += self.frm_per_blk as u64; | |
717 | Ok(NAPacket::new(self.stream.clone().unwrap(), ts, first, dbuf)) | |
718 | } else { | |
719 | Err(EncoderError::TryAgain) | |
720 | } | |
721 | } | |
722 | } | |
723 | ||
724 | impl NAEncoder for CookEncoder { | |
725 | fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> { | |
726 | match encinfo.format { | |
727 | NACodecTypeInfo::None => { | |
728 | Ok(EncodeParameters { | |
729 | format: NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_F32P_FORMAT, 512)), | |
730 | ..Default::default() }) | |
731 | }, | |
732 | NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), | |
733 | NACodecTypeInfo::Audio(ainfo) => { | |
734 | if !self.force_flv { | |
735 | let mut outinfo = ainfo; | |
736 | outinfo.format = SND_F32P_FORMAT; | |
737 | outinfo.sample_rate = match ainfo.sample_rate { | |
738 | 0..= 8000 => 8000, | |
739 | 8001..=11025 => 11025, | |
740 | 11026..=22050 => 22050, | |
741 | _ => 44100, | |
742 | }; | |
743 | if !matches!(outinfo.channels, 1 | 2) && (outinfo.sample_rate != 44100 || !matches!(outinfo.channels, 4 | 5)) { | |
744 | outinfo.channels = 2; | |
745 | } | |
746 | outinfo.block_len = get_block_size(outinfo.sample_rate); | |
747 | let mut ofmt = *encinfo; | |
748 | ofmt.format = NACodecTypeInfo::Audio(outinfo); | |
749 | Ok(ofmt) | |
750 | } else { | |
751 | let flavour = &COOK_FLAVOURS[self.flavour]; | |
752 | let blk_len = match flavour.sample_rate { | |
753 | 44100 => 1024, | |
754 | 22050 => 512, | |
755 | _ => 256, | |
756 | }; | |
757 | let newinfo = NAAudioInfo::new(flavour.sample_rate, flavour.channels, SND_F32P_FORMAT, blk_len); | |
758 | let ofmt = EncodeParameters { | |
759 | format: NACodecTypeInfo::Audio(newinfo), | |
760 | tb_num: blk_len as u32, | |
761 | tb_den: flavour.sample_rate, | |
762 | bitrate: u32::from(flavour.bitrate) * 1000, | |
763 | flags: ENC_MODE_CBR, | |
764 | quality: 0, | |
765 | }; | |
766 | Ok(ofmt) | |
767 | } | |
768 | } | |
769 | } | |
770 | } | |
771 | fn get_capabilities(&self) -> u64 { ENC_CAPS_CBR } | |
772 | fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> { | |
773 | match encinfo.format { | |
774 | NACodecTypeInfo::None => Err(EncoderError::FormatError), | |
775 | NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), | |
776 | NACodecTypeInfo::Audio(ainfo) => { | |
777 | if ainfo.format != SND_F32P_FORMAT || !matches!(ainfo.sample_rate, 8000 | 11025 | 22050 | 44100) { | |
778 | return Err(EncoderError::FormatError); | |
779 | } | |
780 | let blk_size = get_block_size(ainfo.sample_rate); | |
781 | if ainfo.block_len != blk_size { | |
782 | return Err(EncoderError::FormatError); | |
783 | } | |
784 | ||
785 | let bitrate = if encinfo.bitrate > 0 { Some(encinfo.bitrate / 1000) } else { None }; | |
786 | ||
787 | let flavours = if self.g8_only { &COOK_FLAVOURS[..RM8_AUDIO] } else { COOK_FLAVOURS }; | |
788 | let mut cand_id = None; | |
789 | for (i, flavour) in flavours.iter().enumerate().rev() { | |
790 | if flavour.sample_rate == ainfo.sample_rate && flavour.channels == ainfo.channels { | |
791 | if let Some(target) = bitrate { | |
792 | if target <= flavour.bitrate.into() { | |
793 | cand_id = Some(i); | |
794 | if target == flavour.bitrate.into() { | |
795 | break; | |
796 | } | |
797 | } | |
798 | } else { | |
799 | cand_id = Some(i); | |
800 | break; | |
801 | } | |
802 | } | |
803 | } | |
804 | ||
805 | if let Some(id) = cand_id { | |
806 | self.flavour = id; | |
807 | } else { | |
808 | return Err(EncoderError::FormatError); | |
809 | } | |
810 | ||
811 | let flavour = &COOK_FLAVOURS[self.flavour]; | |
812 | self.nframes = flavour.frames_per_blk * flavour.factor; | |
813 | self.frm_per_blk = flavour.frames_per_blk; | |
814 | self.samples.clear(); | |
815 | self.frm_size = blk_size; | |
816 | for _ in 0..flavour.channels { | |
817 | self.samples.push(vec![0.0; self.nframes * self.frm_size]); | |
818 | } | |
819 | self.dsp.init(self.frm_size); | |
820 | ||
821 | self.pairs.clear(); | |
822 | for br_info in flavour.br_infos() { | |
823 | self.pairs.push(CookChannelPair::new(br_info)); | |
824 | } | |
825 | ||
826 | self.tgt_size = 0; | |
827 | for br_info in flavour.br_infos() { | |
828 | self.tgt_size += br_info.frame_bits as usize / 8; | |
829 | } | |
830 | ||
831 | let mut edata = Vec::new(); | |
832 | let mut gw = GrowableMemoryWriter::new_write(&mut edata); | |
833 | let mut bw = ByteWriter::new(&mut gw); | |
834 | if flavour.channels <= 2 { | |
835 | let br_info = &BITRATE_PARAMS[flavour.br_ids[0]]; | |
836 | let ch_mode = if br_info.channels == 1 { | |
837 | 1 | |
838 | } else if br_info.js_bits == 0 { | |
839 | 2 | |
840 | } else { | |
841 | 3 | |
842 | }; | |
843 | bw.write_u32be((1 << 24) | ch_mode)?; | |
844 | bw.write_u16be((self.frm_size as u16) * u16::from(br_info.channels))?; | |
845 | bw.write_u16be(br_info.max_subbands.into())?; | |
846 | if ch_mode == 3 { | |
847 | bw.write_u32be(0)?; // delay | |
848 | bw.write_u16be(br_info.js_start.into())?; | |
849 | bw.write_u16be(br_info.js_bits.into())?; | |
850 | } | |
851 | } else { | |
852 | let chmap: &[u32] = match flavour.channels { | |
853 | 1 => &[ 0x04 ], // C | |
854 | 2 => &[ 0x03 ], // L,R | |
855 | 5 => &[ 0x03, 0x04, 0x30 ], // L,R C Ls,Rs | |
856 | 6 => &[ 0x03, 0x04, 0x08, 0x30 ], // L,R C LFE Ls,Rs | |
857 | _ => unreachable!(), | |
858 | }; | |
859 | for (br_info, &channel_map) in flavour.br_infos().zip(chmap.iter()) { | |
860 | bw.write_u32be(2 << 24)?; | |
861 | bw.write_u16be((self.frm_size as u16) * u16::from(br_info.channels))?; | |
862 | bw.write_u16be(br_info.max_subbands.into())?; | |
863 | bw.write_u32be(0)?; // delay | |
864 | bw.write_u16be(br_info.js_start.into())?; | |
865 | bw.write_u16be(br_info.js_bits.into())?; | |
866 | bw.write_u32be(channel_map)?; | |
867 | } | |
868 | } | |
869 | ||
870 | let soniton = NASoniton::new(16, 0); | |
871 | let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, self.tgt_size); | |
872 | let info = NACodecInfo::new("cook", NACodecTypeInfo::Audio(out_ainfo), Some(edata)); | |
873 | let mut stream = NAStream::new(StreamType::Audio, stream_id, info, blk_size as u32, ainfo.sample_rate, 0); | |
874 | stream.set_num(stream_id as usize); | |
875 | let stream = stream.into_ref(); | |
876 | ||
877 | self.stream = Some(stream.clone()); | |
878 | self.wr_pos = 0; | |
879 | self.rd_pos = 0; | |
880 | ||
881 | Ok(stream) | |
882 | }, | |
883 | } | |
884 | } | |
885 | fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { | |
886 | if let Some(ref abuf) = frm.get_buffer().get_abuf_f32() { | |
887 | if self.rd_pos == self.nframes { | |
888 | return Err(EncoderError::TryAgain); | |
889 | } | |
890 | let stride = abuf.get_stride(); | |
891 | let src = abuf.get_data(); | |
892 | let start = self.rd_pos * self.frm_size; | |
893 | ||
894 | for (dst, src) in self.samples.iter_mut().zip(src.chunks(stride)) { | |
895 | dst[start..][..self.frm_size].copy_from_slice(&src[..self.frm_size]); | |
896 | } | |
897 | self.rd_pos += 1; | |
898 | ||
899 | Ok(()) | |
900 | } else { | |
901 | Err(EncoderError::InvalidParameters) | |
902 | } | |
903 | } | |
904 | fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> { | |
905 | if let Ok(pkt) = self.encode_packet() { | |
906 | Ok(Some(pkt)) | |
907 | } else { | |
908 | Ok(None) | |
909 | } | |
910 | } | |
911 | fn flush(&mut self) -> EncoderResult<()> { | |
912 | if self.wr_pos != 0 { | |
913 | let start = self.wr_pos * self.frm_size; | |
914 | for channel in self.samples.iter_mut() { | |
915 | for el in channel[start..].iter_mut() { | |
916 | *el = 0.0; | |
917 | } | |
918 | } | |
919 | self.wr_pos = self.nframes; | |
920 | } | |
921 | Ok(()) | |
922 | } | |
923 | } | |
924 | ||
925 | const FLAVOUR_OPTION: &str = "flavor"; | |
926 | const G8_ONLY_OPTION: &str = "g8_only"; | |
927 | ||
928 | const ENCODER_OPTS: &[NAOptionDefinition] = &[ | |
929 | NAOptionDefinition { | |
930 | name: FLAVOUR_OPTION, description: "Codec-specific profile (0..32, -1 = auto)", | |
931 | opt_type: NAOptionDefinitionType::Int(Some(-1), Some((COOK_FLAVOURS.len() - 1) as i64)) }, | |
932 | NAOptionDefinition { | |
933 | name: G8_ONLY_OPTION, description: "Force formats present in RealMedia G8", | |
934 | opt_type: NAOptionDefinitionType::Bool }, | |
935 | ]; | |
936 | ||
937 | impl NAOptionHandler for CookEncoder { | |
938 | fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS } | |
939 | fn set_options(&mut self, options: &[NAOption]) { | |
940 | for option in options.iter() { | |
941 | for opt_def in ENCODER_OPTS.iter() { | |
942 | if opt_def.check(option).is_ok() { | |
943 | match option.name { | |
944 | FLAVOUR_OPTION => { | |
945 | if let NAValue::Int(val) = option.value { | |
946 | if val >= 0 { | |
947 | self.flavour = val as usize; | |
948 | self.force_flv = true; | |
949 | } else { | |
950 | self.force_flv = false; | |
951 | } | |
952 | } | |
953 | }, | |
954 | G8_ONLY_OPTION => { | |
955 | if let NAValue::Bool(val) = option.value { | |
956 | self.g8_only = val; | |
957 | } | |
958 | }, | |
959 | _ => {}, | |
960 | }; | |
961 | } | |
962 | } | |
963 | } | |
964 | } | |
965 | fn query_option_value(&self, name: &str) -> Option<NAValue> { | |
966 | match name { | |
967 | FLAVOUR_OPTION => Some(NAValue::Int(self.flavour as i64)), | |
968 | G8_ONLY_OPTION => Some(NAValue::Bool(self.g8_only)), | |
969 | _ => None, | |
970 | } | |
971 | } | |
972 | } | |
973 | ||
974 | pub fn get_encoder() -> Box<dyn NAEncoder + Send> { Box::new(CookEncoder::new()) } | |
975 | ||
976 | struct FlavourInfo { | |
977 | sample_rate: u32, | |
978 | channels: u8, | |
979 | bitrate: u16, | |
980 | frames_per_blk: usize, | |
981 | factor: usize, | |
982 | br_ids: [usize; 4], | |
983 | } | |
984 | ||
985 | impl FlavourInfo { | |
986 | fn br_infos(&self) -> BitrateParamsIterator { | |
987 | BitrateParamsIterator::new(self.br_ids, self.channels) | |
988 | } | |
989 | } | |
990 | ||
991 | struct BitrateParamsIterator { | |
992 | ids: [usize; 4], | |
993 | cur: usize, | |
994 | len: usize, | |
995 | } | |
996 | ||
997 | impl BitrateParamsIterator { | |
998 | fn new(ids: [usize; 4], chans: u8) -> Self { | |
999 | Self { | |
1000 | ids, | |
1001 | cur: 0, | |
1002 | len: usize::from((chans + 1) / 2) | |
1003 | } | |
1004 | } | |
1005 | } | |
1006 | ||
1007 | impl Iterator for BitrateParamsIterator { | |
1008 | type Item = &'static BitrateParams; | |
1009 | fn next(&mut self) -> Option<Self::Item> { | |
1010 | if self.cur < self.len { | |
1011 | let ret = &BITRATE_PARAMS[self.ids[self.cur]]; | |
1012 | self.cur += 1; | |
1013 | Some(ret) | |
1014 | } else { | |
1015 | None | |
1016 | } | |
1017 | } | |
1018 | } | |
1019 | ||
1020 | macro_rules! flavour_desc { | |
1021 | ($srate:expr, $ch:expr, $br:expr, $fpb:expr, $factor:expr, $br_ids:expr) => { | |
1022 | FlavourInfo { | |
1023 | sample_rate: $srate, | |
1024 | channels: $ch, | |
1025 | bitrate: $br, | |
1026 | frames_per_blk: $fpb, | |
1027 | factor: $factor, | |
1028 | br_ids: $br_ids, | |
1029 | } | |
1030 | } | |
1031 | } | |
1032 | ||
1033 | const RM8_AUDIO: usize = 17; // newer variants that use joint-stereo coding and support multichannel | |
1034 | const COOK_FLAVOURS: &[FlavourInfo] = &[ | |
1035 | flavour_desc!( 8000, 1, 8, 9, 8, [ 0, 0, 0, 0]), | |
1036 | flavour_desc!(11025, 1, 11, 11, 8, [ 1, 0, 0, 0]), | |
1037 | flavour_desc!(22050, 1, 16, 12, 8, [ 2, 0, 0, 0]), | |
1038 | flavour_desc!(22050, 1, 20, 10, 9, [ 3, 0, 0, 0]), | |
1039 | flavour_desc!(44100, 1, 32, 7, 14, [ 4, 0, 0, 0]), | |
1040 | flavour_desc!(44100, 1, 44, 5, 16, [ 5, 0, 0, 0]), | |
1041 | flavour_desc!(44100, 1, 64, 4, 20, [ 6, 0, 0, 0]), | |
1042 | flavour_desc!(22050, 1, 32, 6, 16, [ 7, 0, 0, 0]), | |
1043 | flavour_desc!( 8000, 1, 6, 12, 6, [ 8, 0, 0, 0]), | |
1044 | flavour_desc!(11025, 2, 19, 10, 10, [ 9, 0, 0, 0]), | |
1045 | flavour_desc!(22050, 2, 32, 6, 14, [10, 0, 0, 0]), | |
1046 | flavour_desc!(22050, 2, 44, 5, 16, [11, 0, 0, 0]), | |
1047 | flavour_desc!(44100, 2, 64, 4, 20, [12, 0, 0, 0]), | |
1048 | flavour_desc!(44100, 2, 95, 3, 30, [13, 0, 0, 0]), | |
1049 | flavour_desc!(44100, 1, 64, 4, 20, [14, 0, 0, 0]), | |
1050 | flavour_desc!(44100, 1, 20, 10, 9, [15, 0, 0, 0]), | |
1051 | flavour_desc!(44100, 1, 32, 7, 14, [16, 0, 0, 0]), | |
1052 | flavour_desc!(22050, 2, 16, 10, 10, [17, 0, 0, 0]), | |
1053 | flavour_desc!(22050, 2, 20, 10, 10, [18, 0, 0, 0]), | |
1054 | flavour_desc!(22050, 2, 20, 10, 10, [19, 0, 0, 0]), | |
1055 | flavour_desc!(22050, 2, 32, 5, 16, [20, 0, 0, 0]), | |
1056 | flavour_desc!(44100, 2, 32, 5, 16, [21, 0, 0, 0]), | |
1057 | flavour_desc!(44100, 2, 44, 5, 16, [22, 0, 0, 0]), | |
1058 | flavour_desc!(44100, 2, 44, 5, 16, [23, 0, 0, 0]), | |
1059 | flavour_desc!(44100, 2, 64, 5, 16, [24, 0, 0, 0]), | |
1060 | flavour_desc!(44100, 2, 96, 5, 16, [25, 0, 0, 0]), | |
1061 | flavour_desc!(11025, 2, 12, 11, 8, [26, 0, 0, 0]), | |
1062 | flavour_desc!(44100, 2, 64, 5, 16, [27, 0, 0, 0]), | |
1063 | flavour_desc!(44100, 2, 96, 5, 16, [28, 0, 0, 0]), | |
1064 | flavour_desc!(22050, 2, 44, 5, 16, [29, 0, 0, 0]), | |
1065 | flavour_desc!(44100, 5, 96, 5, 16, [21, 16, 21, 0]), | |
1066 | flavour_desc!(44100, 6, 131, 3, 10, [23, 5, 30, 21]), | |
1067 | flavour_desc!(44100, 6, 183, 2, 10, [24, 6, 30, 23]), | |
1068 | flavour_desc!(44100, 6, 268, 1, 1, [25, 6, 30, 25]) | |
1069 | ]; | |
1070 | ||
1071 | struct BitrateParams { | |
1072 | channels: u8, | |
1073 | max_subbands: u8, | |
1074 | frame_bits: u32, | |
1075 | js_start: u8, | |
1076 | js_bits: u8, | |
1077 | } | |
1078 | ||
1079 | macro_rules! br_desc { | |
1080 | ($ch:expr, $bits:expr, $subbands:expr, $js_start:expr, $js_bits:expr) => { | |
1081 | BitrateParams { | |
1082 | channels: $ch, | |
1083 | max_subbands: $subbands, | |
1084 | frame_bits: $bits, | |
1085 | js_start: $js_start, | |
1086 | js_bits: $js_bits, | |
1087 | } | |
1088 | } | |
1089 | } | |
1090 | ||
1091 | const BITRATE_PARAMS: &[BitrateParams] = &[ | |
1092 | br_desc!(1, 256, 12, 0, 0), | |
1093 | br_desc!(1, 256, 12, 0, 0), | |
1094 | br_desc!(1, 376, 18, 0, 0), | |
1095 | br_desc!(1, 480, 23, 0, 0), | |
1096 | br_desc!(1, 744, 37, 0, 0), | |
1097 | br_desc!(1, 1024, 47, 0, 0), | |
1098 | br_desc!(1, 1488, 47, 0, 0), | |
1099 | br_desc!(1, 744, 24, 0, 0), | |
1100 | ||
1101 | br_desc!(1, 192, 9, 0, 0), | |
1102 | br_desc!(2, 464, 11, 0, 0), | |
1103 | br_desc!(2, 752, 18, 0, 0), | |
1104 | br_desc!(2, 1024, 24, 0, 0), | |
1105 | br_desc!(2, 1488, 37, 0, 0), | |
1106 | br_desc!(2, 2224, 47, 0, 0), | |
1107 | br_desc!(1, 1488, 47, 0, 0), | |
1108 | br_desc!(1, 480, 47, 0, 0), | |
1109 | ||
1110 | br_desc!(1, 744, 47, 0, 0), | |
1111 | br_desc!(2, 384, 16, 1, 3), | |
1112 | br_desc!(2, 480, 20, 1, 3), | |
1113 | br_desc!(2, 480, 23, 1, 3), | |
1114 | br_desc!(2, 744, 24, 2, 4), | |
1115 | br_desc!(2, 744, 32, 2, 4), | |
1116 | br_desc!(2, 1024, 32, 5, 5), | |
1117 | br_desc!(2, 1024, 37, 2, 4), | |
1118 | ||
1119 | br_desc!(2, 1488, 37, 6, 5), | |
1120 | br_desc!(2, 2240, 37, 8, 5), | |
1121 | br_desc!(2, 288, 9, 1, 2), | |
1122 | br_desc!(2, 1488, 30, 17, 5), | |
1123 | br_desc!(2, 2240, 34, 19, 5), | |
1124 | br_desc!(2, 1024, 23, 17, 5), | |
1125 | br_desc!(1, 256, 1, 0, 0) | |
1126 | ]; | |
1127 | ||
1128 | #[cfg(test)] | |
1129 | mod test { | |
1130 | use nihav_core::codecs::*; | |
1131 | use nihav_core::demuxers::*; | |
1132 | use nihav_core::muxers::*; | |
1133 | use nihav_codec_support::test::enc_video::*; | |
1134 | use crate::*; | |
1135 | ||
1136 | fn test_encoder(name: &'static str, enc_options: &[NAOption], bitrate: u32, channels: u8, hash: &[u32; 4]) { | |
1137 | let mut dmx_reg = RegisteredDemuxers::new(); | |
1138 | realmedia_register_all_demuxers(&mut dmx_reg); | |
1139 | let mut dec_reg = RegisteredDecoders::new(); | |
1140 | realmedia_register_all_decoders(&mut dec_reg); | |
1141 | let mut mux_reg = RegisteredMuxers::new(); | |
1142 | realmedia_register_all_muxers(&mut mux_reg); | |
1143 | let mut enc_reg = RegisteredEncoders::new(); | |
1144 | realmedia_register_all_encoders(&mut enc_reg); | |
1145 | ||
1146 | // sample from a private collection | |
1147 | let dec_config = DecoderTestParams { | |
1148 | demuxer: "realmedia", | |
1149 | in_name: "assets/RV/rv30_weighted_mc.rm", | |
1150 | stream_type: StreamType::Audio, | |
1151 | limit: None, | |
1152 | dmx_reg, dec_reg, | |
1153 | }; | |
1154 | let enc_config = EncoderTestParams { | |
1155 | muxer: "realmedia", | |
1156 | enc_name: "cook", | |
1157 | out_name: name, | |
1158 | mux_reg, enc_reg, | |
1159 | }; | |
1160 | let dst_ainfo = NAAudioInfo { | |
1161 | sample_rate: 0, | |
1162 | channels, | |
1163 | format: SND_F32P_FORMAT, | |
1164 | block_len: 1024, | |
1165 | }; | |
1166 | let enc_params = EncodeParameters { | |
1167 | format: NACodecTypeInfo::Audio(dst_ainfo), | |
1168 | quality: 0, | |
1169 | bitrate, | |
1170 | tb_num: 0, | |
1171 | tb_den: 0, | |
1172 | flags: 0, | |
1173 | }; | |
1174 | //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options); | |
1175 | ||
1176 | test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, hash); | |
1177 | } | |
1178 | #[test] | |
1179 | fn test_cook_encoder_mono() { | |
1180 | let enc_options = &[ | |
1181 | ]; | |
1182 | test_encoder("cook-mono.rma", enc_options, 0, 1, | |
1183 | &[0xa07cffaa, 0x257e8ca1, 0x27f58960, 0xb48f64f4]); | |
1184 | } | |
1185 | #[test] | |
1186 | fn test_cook_encoder_stereo() { | |
1187 | let enc_options = &[ | |
1188 | NAOption { name: super::G8_ONLY_OPTION, value: NAValue::Bool(true) }, | |
1189 | ]; | |
1190 | test_encoder("cook-stereo.rma", enc_options, 0, 2, | |
1191 | &[0xbc407879, 0x18c7b334, 0xc0299482, 0x89c6b953]); | |
1192 | } | |
1193 | #[test] | |
1194 | fn test_cook_encoder_jstereo_32kbps() { | |
1195 | let enc_options = &[ | |
1196 | ]; | |
1197 | test_encoder("cook-jstereo32.rma", enc_options, 32000, 2, | |
1198 | &[0xe7526c4e, 0xda095156, 0x840a74f7, 0x0e9a1603]); | |
1199 | } | |
1200 | #[test] | |
1201 | fn test_cook_encoder_jstereo_64kbps() { | |
1202 | let enc_options = &[ | |
1203 | ]; | |
1204 | test_encoder("cook-jstereo64.rma", enc_options, 64000, 2, | |
1205 | &[0x09688175, 0x9abe1aac, 0x3c3f15fb, 0x066b99f0]); | |
1206 | } | |
1207 | #[test] | |
1208 | fn test_cook_encoder_jstereo_96kbps() { | |
1209 | let enc_options = &[ | |
1210 | ]; | |
1211 | test_encoder("cook-jstereo96.rma", enc_options, 96000, 2, | |
1212 | &[0x51829ff8, 0x00017191, 0x5bc95490, 0xd1d03faf]); | |
1213 | } | |
1214 | } |