| 1 | use nihav_core::codecs::*; |
| 2 | use nihav_core::io::bitreader::*; |
| 3 | use nihav_codec_support::codecs::imaadpcm::*; |
| 4 | use std::str::FromStr; |
| 5 | |
| 6 | const STEPS: [&[i8]; 4] = [ |
| 7 | &[ -1, 2], |
| 8 | &[ -1, -1, 2, 4], |
| 9 | &[ -1, -1, -1, -1, 2, 4, 6, 8], |
| 10 | &[ -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16 ] |
| 11 | ]; |
| 12 | |
| 13 | #[derive(Clone,Copy)] |
| 14 | struct State { |
| 15 | predictor: i32, |
| 16 | step: usize, |
| 17 | smask: u8, |
| 18 | steps: &'static [i8], |
| 19 | } |
| 20 | |
| 21 | impl State { |
| 22 | fn new() -> Self { |
| 23 | Self { |
| 24 | predictor: 0, |
| 25 | step: 0, |
| 26 | smask: 0, |
| 27 | steps: STEPS[2], |
| 28 | } |
| 29 | } |
| 30 | fn expand_sample(&mut self, nibble: u8) -> i16 { |
| 31 | let istep = (self.step as isize) + isize::from(self.steps[(nibble & !self.smask) as usize]); |
| 32 | let sign = (nibble & self.smask) != 0; |
| 33 | let diff = (i32::from(2 * (nibble & !self.smask) + 1) * IMA_STEP_TABLE[self.step]) >> 3; |
| 34 | let sample = if !sign { self.predictor + diff } else { self.predictor - diff }; |
| 35 | self.predictor = sample.max(i32::from(std::i16::MIN)).min(i32::from(std::i16::MAX)); |
| 36 | self.step = istep.max(0).min(IMA_MAX_STEP as isize) as usize; |
| 37 | self.predictor as i16 |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | const BLOCK_LEN: usize = 4096; |
| 42 | |
| 43 | struct ADPCMDecoder { |
| 44 | ainfo: NAAudioInfo, |
| 45 | chmap: NAChannelMap, |
| 46 | ch_state: [State; 2], |
| 47 | } |
| 48 | |
| 49 | impl ADPCMDecoder { |
| 50 | fn new() -> Self { |
| 51 | Self { |
| 52 | ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, BLOCK_LEN), |
| 53 | chmap: NAChannelMap::new(), |
| 54 | ch_state: [State::new(), State::new()], |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | impl NADecoder for ADPCMDecoder { |
| 60 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { |
| 61 | if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { |
| 62 | let channels = ainfo.get_channels() as usize; |
| 63 | validate!(channels == 2 || channels == 1); |
| 64 | self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, 0); |
| 65 | self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap(); |
| 66 | Ok(()) |
| 67 | } else { |
| 68 | Err(DecoderError::InvalidData) |
| 69 | } |
| 70 | } |
| 71 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { |
| 72 | let info = pkt.get_stream().get_info(); |
| 73 | if let NACodecTypeInfo::Audio(_) = info.get_properties() { |
| 74 | let src = pkt.get_buffer(); |
| 75 | let channels = self.chmap.num_channels(); |
| 76 | let mut br = BitReader::new(&src, BitReaderMode::BE); |
| 77 | let step_size = br.read(2)? as usize + 2; |
| 78 | |
| 79 | let pkt_size = (16 + 6 + (BLOCK_LEN - 1) * step_size) * channels; |
| 80 | let num_pkts = ((br.left() as usize) / pkt_size).max(1); |
| 81 | let nsamples = num_pkts * BLOCK_LEN; |
| 82 | let mut abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?; |
| 83 | let mut adata = abuf.get_abuf_i16().unwrap(); |
| 84 | let mut off = [adata.get_offset(0), adata.get_offset(1)]; |
| 85 | let dst = adata.get_data_mut().unwrap(); |
| 86 | |
| 87 | let mut tot_samples = 0; |
| 88 | for _pkt in 0..num_pkts { |
| 89 | for (ch, state) in self.ch_state[..channels].iter_mut().enumerate() { |
| 90 | state.predictor = br.read_s(16)?; |
| 91 | state.step = br.read(6)? as usize; |
| 92 | state.steps = STEPS[step_size - 2]; |
| 93 | state.smask = 1 << (step_size - 1); |
| 94 | dst[off[ch]] = state.predictor as i16; |
| 95 | off[ch] += 1; |
| 96 | } |
| 97 | tot_samples += 1; |
| 98 | let cur_samples = ((br.left() as usize) / step_size / channels).min(BLOCK_LEN - 1); |
| 99 | for _ in 0..cur_samples { |
| 100 | for (ch, state) in self.ch_state[..channels].iter_mut().enumerate() { |
| 101 | let idx = br.read(step_size as u8)? as u8; |
| 102 | dst[off[ch]] = state.expand_sample(idx); |
| 103 | off[ch] += 1; |
| 104 | } |
| 105 | } |
| 106 | tot_samples += cur_samples; |
| 107 | } |
| 108 | abuf.truncate_audio(tot_samples); |
| 109 | |
| 110 | let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf); |
| 111 | frm.set_duration(Some(tot_samples as u64)); |
| 112 | frm.set_keyframe(true); |
| 113 | Ok(frm.into_ref()) |
| 114 | } else { |
| 115 | Err(DecoderError::InvalidData) |
| 116 | } |
| 117 | } |
| 118 | fn flush(&mut self) { |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | impl NAOptionHandler for ADPCMDecoder { |
| 123 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } |
| 124 | fn set_options(&mut self, _options: &[NAOption]) { } |
| 125 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } |
| 126 | } |
| 127 | |
| 128 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { |
| 129 | Box::new(ADPCMDecoder::new()) |
| 130 | } |
| 131 | |
| 132 | #[cfg(test)] |
| 133 | mod test { |
| 134 | use nihav_core::codecs::RegisteredDecoders; |
| 135 | use nihav_core::demuxers::RegisteredDemuxers; |
| 136 | use nihav_codec_support::test::dec_video::*; |
| 137 | use crate::flash_register_all_decoders; |
| 138 | use crate::flash_register_all_demuxers; |
| 139 | #[test] |
| 140 | fn test_flv_adpcm_mono() { |
| 141 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 142 | flash_register_all_demuxers(&mut dmx_reg); |
| 143 | let mut dec_reg = RegisteredDecoders::new(); |
| 144 | flash_register_all_decoders(&mut dec_reg); |
| 145 | |
| 146 | // sample: https://samples.mplayerhq.hu/FLV/flash_flv_adpcm_testfiles/mono_11k.flv |
| 147 | test_decoding("flv", "flv-adpcm", "assets/Flash/mono_11k.flv", Some(3000), &dmx_reg, &dec_reg, |
| 148 | ExpectedTestResult::MD5([0x4cf30e71, 0x4360c85b, 0x21c52863, 0x1782160e])); |
| 149 | } |
| 150 | #[test] |
| 151 | fn test_flv_adpcm_stereo() { |
| 152 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 153 | flash_register_all_demuxers(&mut dmx_reg); |
| 154 | let mut dec_reg = RegisteredDecoders::new(); |
| 155 | flash_register_all_decoders(&mut dec_reg); |
| 156 | |
| 157 | // sample: https://samples.mplayerhq.hu/FLV/flash_flv_adpcm_testfiles/stereo_44k.flv |
| 158 | test_decoding("flv", "flv-adpcm", "assets/Flash/stereo_44k.flv", Some(3000), &dmx_reg, &dec_reg, |
| 159 | ExpectedTestResult::MD5([0xae108d38, 0xb36236f8, 0x2bc18d31, 0xac600424])); |
| 160 | } |
| 161 | } |