Flash support
[nihav.git] / nihav-flash / src / codecs / adpcm.rs
diff --git a/nihav-flash/src/codecs/adpcm.rs b/nihav-flash/src/codecs/adpcm.rs
new file mode 100644 (file)
index 0000000..e6e21cf
--- /dev/null
@@ -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<NAFrameRef> {
+        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<NAValue> { None }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    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]));
+    }
+}