X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-flash%2Fsrc%2Fcodecs%2Fadpcm.rs;fp=nihav-flash%2Fsrc%2Fcodecs%2Fadpcm.rs;h=e6e21cfd583f7f8da5eac531538e5f5a92741a52;hp=0000000000000000000000000000000000000000;hb=92d9fb6993d2d3f6f7a016ee6796a98e6e989f21;hpb=bc23de6bedc2e151caea241b073a65d30f62c134 diff --git a/nihav-flash/src/codecs/adpcm.rs b/nihav-flash/src/codecs/adpcm.rs new file mode 100644 index 0000000..e6e21cf --- /dev/null +++ b/nihav-flash/src/codecs/adpcm.rs @@ -0,0 +1,159 @@ +use nihav_core::codecs::*; +use nihav_core::io::bitreader::*; +use nihav_codec_support::codecs::imaadpcm::*; +use std::str::FromStr; + +const STEPS: [&[i8]; 4] = [ + &[ -1, 2], + &[ -1, -1, 2, 4], + &[ -1, -1, -1, -1, 2, 4, 6, 8], + &[ -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16 ] +]; + +#[derive(Clone,Copy)] +struct State { + predictor: i32, + step: usize, + smask: u8, + steps: &'static [i8], +} + +impl State { + fn new() -> Self { + Self { + predictor: 0, + step: 0, + smask: 0, + steps: STEPS[2], + } + } + fn expand_sample(&mut self, nibble: u8) -> i16 { + let istep = (self.step as isize) + isize::from(self.steps[(nibble & !self.smask) as usize]); + let sign = (nibble & self.smask) != 0; + let diff = (i32::from(2 * (nibble & !self.smask) + 1) * IMA_STEP_TABLE[self.step]) >> 3; + let sample = if !sign { self.predictor + diff } else { self.predictor - diff }; + self.predictor = sample.max(i32::from(std::i16::MIN)).min(i32::from(std::i16::MAX)); + self.step = istep.max(0).min(IMA_MAX_STEP as isize) as usize; + self.predictor as i16 + } +} + +const BLOCK_LEN: usize = 4096; + +struct ADPCMDecoder { + ainfo: NAAudioInfo, + chmap: NAChannelMap, + ch_state: [State; 2], +} + +impl ADPCMDecoder { + fn new() -> Self { + Self { + ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, BLOCK_LEN), + chmap: NAChannelMap::new(), + ch_state: [State::new(), State::new()], + } + } +} + +impl NADecoder for ADPCMDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { + let channels = ainfo.get_channels() as usize; + validate!(channels == 2 || channels == 1); + self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, 0); + self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap(); + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { + let info = pkt.get_stream().get_info(); + if let NACodecTypeInfo::Audio(_) = info.get_properties() { + let src = pkt.get_buffer(); + let channels = self.chmap.num_channels(); + let mut br = BitReader::new(&src, BitReaderMode::BE); + let step_size = br.read(2)? as usize + 2; + + let pkt_size = (16 + 6 + (BLOCK_LEN - 1) * step_size) * channels; + let num_pkts = ((br.left() as usize) / pkt_size).max(1); + let nsamples = num_pkts * BLOCK_LEN; + let mut abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?; + let mut adata = abuf.get_abuf_i16().unwrap(); + let mut off = [adata.get_offset(0), adata.get_offset(1)]; + let dst = adata.get_data_mut().unwrap(); + + let mut tot_samples = 0; + for _pkt in 0..num_pkts { + for (ch, state) in self.ch_state[..channels].iter_mut().enumerate() { + state.predictor = br.read_s(16)?; + state.step = br.read(6)? as usize; + state.steps = STEPS[step_size - 2]; + state.smask = 1 << (step_size - 1); + dst[off[ch]] = state.predictor as i16; + off[ch] += 1; + } + tot_samples += 1; + let cur_samples = ((br.left() as usize) / step_size / channels).min(BLOCK_LEN - 1); + for _ in 0..cur_samples { + for (ch, state) in self.ch_state[..channels].iter_mut().enumerate() { + let idx = br.read(step_size as u8)? as u8; + dst[off[ch]] = state.expand_sample(idx); + off[ch] += 1; + } + } + tot_samples += cur_samples; + } + abuf.truncate_audio(tot_samples); + + let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf); + frm.set_duration(Some(tot_samples as u64)); + frm.set_keyframe(true); + Ok(frm.into_ref()) + } else { + Err(DecoderError::InvalidData) + } + } + fn flush(&mut self) { + } +} + +impl NAOptionHandler for ADPCMDecoder { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + +pub fn get_decoder() -> Box { + Box::new(ADPCMDecoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_codec_support::test::dec_video::*; + use crate::flash_register_all_decoders; + use crate::flash_register_all_demuxers; + #[test] + fn test_flv_adpcm_mono() { + let mut dmx_reg = RegisteredDemuxers::new(); + flash_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + flash_register_all_decoders(&mut dec_reg); + + test_decoding("flv", "flv-adpcm", "assets/Flash/mono_11k.flv", Some(3000), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x4cf30e71, 0x4360c85b, 0x21c52863, 0x1782160e])); + } + #[test] + fn test_flv_adpcm_stereo() { + let mut dmx_reg = RegisteredDemuxers::new(); + flash_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + flash_register_all_decoders(&mut dec_reg); + + test_decoding("flv", "flv-adpcm", "assets/Flash/stereo_44k.flv", Some(3000), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0xae108d38, 0xb36236f8, 0x2bc18d31, 0xac600424])); + } +}