From: Kostya Shishkov Date: Sun, 24 May 2020 13:19:43 +0000 (+0200) Subject: add a crate for common MS formats X-Git-Url: https://git.nihav.org/?p=nihav.git;a=commitdiff_plain;h=4abcd84283e5d7168cc495e41cbc443710bfbd5e add a crate for common MS formats --- diff --git a/nihav-allstuff/Cargo.toml b/nihav-allstuff/Cargo.toml index b8ef869..49aeaae 100644 --- a/nihav-allstuff/Cargo.toml +++ b/nihav-allstuff/Cargo.toml @@ -10,6 +10,7 @@ nihav_commonfmt = { path = "../nihav-commonfmt" } nihav_duck = { path = "../nihav-duck" } nihav_game = { path = "../nihav-game" } nihav_indeo = { path = "../nihav-indeo" } +nihav_ms = { path = "../nihav-ms" } nihav_rad = { path = "../nihav-rad" } nihav_realmedia = { path = "../nihav-realmedia" } diff --git a/nihav-allstuff/src/lib.rs b/nihav-allstuff/src/lib.rs index 4c2214a..759ef54 100644 --- a/nihav-allstuff/src/lib.rs +++ b/nihav-allstuff/src/lib.rs @@ -4,6 +4,7 @@ extern crate nihav_commonfmt; extern crate nihav_duck; extern crate nihav_game; extern crate nihav_indeo; +extern crate nihav_ms; extern crate nihav_rad; extern crate nihav_realmedia; @@ -20,6 +21,8 @@ use nihav_game::game_register_all_demuxers; use nihav_indeo::indeo_register_all_codecs; +use nihav_ms::ms_register_all_codecs; + use nihav_rad::rad_register_all_codecs; use nihav_rad::rad_register_all_demuxers; @@ -32,6 +35,7 @@ pub fn nihav_register_all_codecs(rd: &mut RegisteredDecoders) { duck_register_all_codecs(rd); game_register_all_codecs(rd); indeo_register_all_codecs(rd); + ms_register_all_codecs(rd); rad_register_all_codecs(rd); realmedia_register_all_codecs(rd); } diff --git a/nihav-ms/Cargo.toml b/nihav-ms/Cargo.toml new file mode 100644 index 0000000..6c925ad --- /dev/null +++ b/nihav-ms/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "nihav_ms" +version = "0.1.0" +authors = ["Kostya Shishkov "] +edition = "2018" + +[dependencies.nihav_core] +path = "../nihav-core" +features = [] + +[dependencies.nihav_codec_support] +path = "../nihav-codec-support" +features = [] + +[dev-dependencies] +nihav_commonfmt = { path = "../nihav-commonfmt" } + +[features] +default = ["all_decoders"] +all_decoders = ["all_video_decoders", "all_audio_decoders"] +decoders = [] + +all_video_decoders = ["decoder_msvideo1"] +decoder_msvideo1 = ["decoders"] + +all_audio_decoders = ["decoder_ima_adpcm_ms", "decoder_ms_adpcm"] +decoder_ima_adpcm_ms = ["decoders"] +decoder_ms_adpcm = ["decoders"] diff --git a/nihav-ms/src/codecs/imaadpcm.rs b/nihav-ms/src/codecs/imaadpcm.rs new file mode 100644 index 0000000..42730ee --- /dev/null +++ b/nihav-ms/src/codecs/imaadpcm.rs @@ -0,0 +1,106 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; +use nihav_codec_support::codecs::imaadpcm::*; +use std::str::FromStr; + +struct IMAADPCMDecoder { + ainfo: NAAudioInfo, + chmap: NAChannelMap, + ch_state: [IMAState; 2], + block_len: usize, +} + +impl IMAADPCMDecoder { + fn new() -> Self { + Self { + ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0), + chmap: NAChannelMap::new(), + ch_state: [IMAState::new(), IMAState::new()], + block_len: 0, + } + } +} + +impl NADecoder for IMAADPCMDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { + self.block_len = ainfo.get_block_len(); + let channels = ainfo.get_channels() as usize; + validate!(channels == 2 || channels == 1); + validate!(self.block_len >= 8 * channels); + validate!((self.block_len & 3) == 0); + let len = (self.block_len / channels - 4) * 2 + 1; + self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, len); + 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 pktbuf = pkt.get_buffer(); + let channels = self.chmap.num_channels(); + validate!(pktbuf.len() >= 8 * channels); + let nsamples = (pktbuf.len() / channels - 4) * 2 + 1; + let 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 mr = MemoryReader::new_read(pktbuf.as_slice()); + let mut br = ByteReader::new(&mut mr); + for ch in 0..channels { + let pred = br.read_u16le()? as i16; + let step = br.read_byte()?; + br.read_skip(1)?; + validate!(step <= IMA_MAX_STEP); + self.ch_state[ch].reset(pred, step); + dst[off[ch]] = pred; + off[ch] += 1; + } + while br.left() > 0 { + for ch in 0..channels { + let mut cw = br.read_u32le()?; + for _ in 0..8 { + dst[off[ch]] = self.ch_state[ch].expand_sample((cw & 0xF) as u8); + off[ch] += 1; + cw >>= 4; + } + } + } + let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf); + frm.set_duration(Some(nsamples as u64)); + frm.set_keyframe(false); + Ok(frm.into_ref()) + } else { + Err(DecoderError::InvalidData) + } + } + fn flush(&mut self) { + } +} + +pub fn get_decoder() -> Box { + Box::new(IMAADPCMDecoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_codec_support::test::dec_video::*; + use crate::ms_register_all_codecs; + use nihav_commonfmt::generic_register_all_demuxers; + #[test] + fn test_ima_adpcm_ms() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + ms_register_all_codecs(&mut dec_reg); + + test_decoding("avi", "ima-adpcm-ms", "assets/MS/IMAG0006.AVI", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x0cdc640f, 0xb00df235, 0x1ec4a280, 0x065b5e9e])); + } +} diff --git a/nihav-ms/src/codecs/mod.rs b/nihav-ms/src/codecs/mod.rs new file mode 100644 index 0000000..0e1a237 --- /dev/null +++ b/nihav-ms/src/codecs/mod.rs @@ -0,0 +1,30 @@ +use nihav_core::codecs::*; + +macro_rules! validate { + ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DecoderError::InvalidData); } }; +} + +#[cfg(feature="decoder_msvideo1")] +pub mod msvideo1; + +#[cfg(feature="decoder_ima_adpcm_ms")] +pub mod imaadpcm; + +#[cfg(feature="decoder_ms_adpcm")] +pub mod msadpcm; + +const MS_CODECS: &[DecoderInfo] = &[ +#[cfg(feature="decoder_msvideo1")] + DecoderInfo { name: "msvideo1", get_decoder: msvideo1::get_decoder }, +#[cfg(feature="decoder_ima_adpcm_ms")] + DecoderInfo { name: "ima-adpcm-ms", get_decoder: imaadpcm::get_decoder }, +#[cfg(feature="decoder_ms_adpcm")] + DecoderInfo { name: "ms-adpcm", get_decoder: msadpcm::get_decoder }, +]; + +/// Registers all available codecs provided by this crate. +pub fn ms_register_all_codecs(rd: &mut RegisteredDecoders) { + for decoder in MS_CODECS.iter() { + rd.add_decoder(decoder.clone()); + } +} diff --git a/nihav-ms/src/codecs/msadpcm.rs b/nihav-ms/src/codecs/msadpcm.rs new file mode 100644 index 0000000..687e0bd --- /dev/null +++ b/nihav-ms/src/codecs/msadpcm.rs @@ -0,0 +1,178 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; +use std::str::FromStr; + +const ADAPT_TABLE: [i32; 16] = [ + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +]; +const ADAPT_COEFFS: [[i32; 2]; 7] = [ + [ 256, 0 ], [ 512, -256 ], [ 0, 0 ], [ 192, 64 ], + [ 240, 0 ], [ 460, -208 ], [ 392, -232 ] +]; + +#[derive(Default)] +struct Predictor { + sample1: i32, + sample2: i32, + delta: i32, + coef1: i32, + coef2: i32, +} + +impl Predictor { + fn expand_nibble(&mut self, nibble: u8) -> i16 { + let mul = if (nibble & 8) == 0 { i32::from(nibble) } else { i32::from(nibble) - 16 }; + let pred = ((self.sample1.wrapping_mul(self.coef1) + self.sample2.wrapping_mul(self.coef2)) >> 8) + self.delta.wrapping_mul(mul); + self.sample2 = self.sample1; + self.sample1 = pred.max(-0x8000).min(0x7FFF); + self.delta = (ADAPT_TABLE[nibble as usize].wrapping_mul(self.delta) >> 8).max(16); + self.sample1 as i16 + } +} + +struct MSADPCMDecoder { + ainfo: NAAudioInfo, + chmap: NAChannelMap, + adapt_coeffs: Vec<[i32; 2]>, + block_len: usize, + block_samps: usize, +} + +impl MSADPCMDecoder { + fn new() -> Self { + Self { + ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0), + chmap: NAChannelMap::new(), + adapt_coeffs: Vec::with_capacity(7), + block_len: 0, + block_samps: 0, + } + } +} + +impl NADecoder for MSADPCMDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { + self.block_len = ainfo.get_block_len(); + let channels = ainfo.get_channels() as usize; + validate!(channels == 2 || channels == 1); + validate!(self.block_len >= 7 * channels + 1); + self.block_samps = (self.block_len / channels - 7) * 2 + 2; + self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, self.block_samps); + self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap(); + self.adapt_coeffs.truncate(0); + if let Some(ref buf) = info.get_extradata() { + validate!(buf.len() >= 6); + validate!((buf.len() & 3) == 0); + let mut mr = MemoryReader::new_read(buf.as_slice()); + let mut br = ByteReader::new(&mut mr); + let _smth = br.read_u16le()?; + let ncoeffs = br.read_u16le()? as usize; + validate!(buf.len() == ncoeffs * 4 + 4); + + for _ in 0..ncoeffs { + let pair = [ + i32::from(br.read_u16le()? as i16), + i32::from(br.read_u16le()? as i16)]; + self.adapt_coeffs.push(pair); + } + } else { + self.adapt_coeffs.extend_from_slice(&ADAPT_COEFFS); + } + 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 pktbuf = pkt.get_buffer(); + let channels = self.chmap.num_channels(); + validate!(pktbuf.len() > 0 && (pktbuf.len() % self.block_len) == 0); + let nblocks = pktbuf.len() / self.block_len; + let nsamples = nblocks * self.block_samps; + let 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 pred = [Predictor::default(), Predictor::default()]; + + for blk in pktbuf.chunks(self.block_len) { + let mut mr = MemoryReader::new_read(blk); + let mut br = ByteReader::new(&mut mr); + for ch in 0..channels { + let coef_idx = br.read_byte()? as usize; + validate!(coef_idx < self.adapt_coeffs.len()); + pred[ch].coef1 = self.adapt_coeffs[coef_idx][0]; + pred[ch].coef2 = self.adapt_coeffs[coef_idx][1]; + } + for ch in 0..channels { + pred[ch].delta = i32::from(br.read_u16le()?); + } + for ch in 0..channels { + let samp = br.read_u16le()? as i16; + pred[ch].sample1 = i32::from(samp); + dst[off[ch]] = samp; + off[ch] += 1; + } + for ch in 0..channels { + let samp = br.read_u16le()? as i16; + pred[ch].sample2 = i32::from(samp); + dst[off[ch]] = samp; + off[ch] += 1; + } + if channels == 1 { + while br.left() > 0 { + let idx = br.read_byte()?; + dst[off[0]] = pred[0].expand_nibble(idx >> 4); + off[0] += 1; + dst[off[0]] = pred[0].expand_nibble(idx & 0xF); + off[0] += 1; + } + } else { + while br.left() > 0 { + let idx = br.read_byte()?; + dst[off[0]] = pred[0].expand_nibble(idx >> 4); + off[0] += 1; + dst[off[1]] = pred[1].expand_nibble(idx & 0xF); + off[1] += 1; + } + } + } + let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf); + frm.set_duration(Some(nsamples as u64)); + frm.set_keyframe(false); + Ok(frm.into_ref()) + } else { + Err(DecoderError::InvalidData) + } + } + fn flush(&mut self) { + } +} + +pub fn get_decoder() -> Box { + Box::new(MSADPCMDecoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_codec_support::test::dec_video::*; + use crate::ms_register_all_codecs; + use nihav_commonfmt::generic_register_all_demuxers; + #[test] + fn test_ms_adpcm() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + ms_register_all_codecs(&mut dec_reg); + + test_decoding("avi", "ms-adpcm", "assets/MS/dance.avi", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x9d6619e1, 0x60d83560, 0xfe5c1fb7, 0xad5d130d])); + } +} diff --git a/nihav-ms/src/codecs/msvideo1.rs b/nihav-ms/src/codecs/msvideo1.rs new file mode 100644 index 0000000..b7bea58 --- /dev/null +++ b/nihav-ms/src/codecs/msvideo1.rs @@ -0,0 +1,312 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; +use nihav_codec_support::codecs::HAMShuffler; + +struct HAMShuffler16 { + lastframe: Option>, +} + +impl HAMShuffler16 { + fn clear(&mut self) { self.lastframe = None; } + fn add_frame(&mut self, buf: NAVideoBufferRef) { + self.lastframe = Some(buf); + } + fn clone_ref(&mut self) -> Option> { + if let Some(ref mut frm) = self.lastframe { + let newfrm = frm.copy_buffer(); + *frm = newfrm.clone().into_ref(); + Some(newfrm.into_ref()) + } else { + None + } + } + fn get_output_frame(&mut self) -> Option> { + match self.lastframe { + Some(ref frm) => Some(frm.clone()), + None => None, + } + } +} + +impl Default for HAMShuffler16 { + fn default() -> Self { Self { lastframe: None } } +} + +const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton { + model: ColorModel::RGB(RGBSubmodel::RGB), components: 3, + comp_info: [ + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }), + None, None], + elem_size: 2, be: false, alpha: false, palette: false }; + +#[derive(Default)] +struct Video1Decoder { + info: NACodecInfoRef, + hams: HAMShuffler, + hams16: HAMShuffler16, + width: usize, + height: usize, + is_16bit: bool, +} + +impl Video1Decoder { + fn new() -> Self { + Self::default() + } + fn decode_frame(&self, br: &mut ByteReader, frm: &mut NASimpleVideoFrame) -> DecoderResult { + let off = frm.offset[0]; + let stride = frm.stride[0]; + let mut skip_count = 0; + let blk_w = (self.width + 3) >> 2; + let blk_h = (self.height + 3) >> 2; + let mut cur_x = 0; + let mut cur_y = 0; + while cur_x < blk_w && cur_y < blk_h { + let op = br.read_u16le()?; + let advance; + if op < 0x8000 { + let mut clr = [0; 2]; + br.read_buf(&mut clr)?; + let mut flags = !op as usize; + let cur_off = off + cur_x * 4 + cur_y * 4 * stride; + for j in 0..4 { + for i in 0..4 { + frm.data[cur_off + i + j * stride] = clr[flags & 1]; + flags >>= 1; + } + } + advance = 1; + } else if op < 0x8400 { + let cur_off = off + cur_x * 4 + cur_y * 4 * stride; + let clr = op as u8; + for j in 0..4 { + for i in 0..4 { + frm.data[cur_off + i + j * stride] = clr; + } + } + advance = 1; + } else if op < 0x8800 { + advance = (op & 0x3FF) as usize; + validate!(advance > 0); + skip_count += advance; + } else { + let mut clr = [0; 8]; + br.read_buf(&mut clr)?; + let mut flags = !op as usize; + let cur_off = off + cur_x * 4 + cur_y * 4 * stride; + for j in 0..4 { + for i in 0..4 { + frm.data[cur_off + i + j * stride] = clr[(i >> 1) * 2 + (j >> 1) * 4 + (flags & 1)]; + flags >>= 1; + } + } + advance = 1; + } + cur_x += advance; + while cur_x >= blk_w { + cur_x -= blk_w; + cur_y += 1; + } + } + validate!(cur_x == 0 && cur_y == blk_h); + if skip_count == 0 { + Ok(FrameType::I) + } else if skip_count < blk_w * blk_h { + Ok(FrameType::P) + } else { + Ok(FrameType::Skip) + } + } + fn decode_frame16(&self, br: &mut ByteReader, frm: &mut NASimpleVideoFrame) -> DecoderResult { + let off = frm.offset[0]; + let stride = frm.stride[0]; + let mut skip_count = 0; + let blk_w = (self.width + 3) >> 2; + let blk_h = (self.height + 3) >> 2; + let mut cur_x = 0; + let mut cur_y = 0; + while cur_x < blk_w && cur_y < blk_h { + let op = br.read_u16le()?; + let advance; + if (op & 0x8000) == 0 { + let mut clr = [0; 8]; + clr[0] = br.read_u16le()?; + clr[1] = br.read_u16le()?; + let mut flags = !op as usize; + let cur_off = off + cur_x * 4 + cur_y * 4 * stride; + if (clr[0] & 0x8000) == 0 { + for j in 0..4 { + for i in 0..4 { + frm.data[cur_off + i + j * stride] = clr[flags & 1]; + flags >>= 1; + } + } + } else { + clr[2] = br.read_u16le()?; + clr[3] = br.read_u16le()?; + clr[4] = br.read_u16le()?; + clr[5] = br.read_u16le()?; + clr[6] = br.read_u16le()?; + clr[7] = br.read_u16le()?; + for j in 0..4 { + for i in 0..4 { + frm.data[cur_off + i + j * stride] = clr[(i >> 1) * 2 + (j >> 1) * 4 + (flags & 1)]; + flags >>= 1; + } + } + } + advance = 1; + } else if (op & 0xFC00) == 0x8400 { + advance = (op & 0x3FF) as usize; + validate!(advance > 0); + skip_count += advance; + } else { + let cur_off = off + cur_x * 4 + cur_y * 4 * stride; + let clr = op & 0x7FFF; + for j in 0..4 { + for i in 0..4 { + frm.data[cur_off + i + j * stride] = clr; + } + } + advance = 1; + } + cur_x += advance; + while cur_x >= blk_w { + cur_x -= blk_w; + cur_y += 1; + } + } + validate!((cur_x == 0 || cur_x == 1) && cur_y == blk_h); + if skip_count == 0 { + Ok(FrameType::I) + } else if skip_count < blk_w * blk_h { + Ok(FrameType::P) + } else { + Ok(FrameType::Skip) + } + } +} + +impl NADecoder for Video1Decoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { +println!("fmt {}", vinfo.get_format()); + self.is_16bit = !vinfo.get_format().palette; + let fmt = if !self.is_16bit { PAL8_FORMAT } else { RGB555_FORMAT }; + self.width = vinfo.get_width(); + self.height = vinfo.get_height(); + let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.width, self.height, true, fmt)); + self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); + + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { + let src = pkt.get_buffer(); + validate!(src.len() >= 2); + let mut mr = MemoryReader::new_read(src.as_slice()); + let mut br = ByteReader::new(&mut mr); + + let buftype; + let ftype; + if !self.is_16bit { + let bufret = self.hams.clone_ref(); + let mut buf; + if let Some(bbuf) = bufret { + buf = bbuf; + } else { + let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 2)?; + buf = bufinfo.get_vbuf().unwrap(); + self.hams.add_frame(buf); + buf = self.hams.get_output_frame().unwrap(); + } + let mut frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap(); + ftype = self.decode_frame(&mut br, &mut frm)?; + let paloff = frm.offset[1]; + let dpal = &mut frm.data[paloff..]; +let mut found_pal = false; + for sd in pkt.side_data.iter() { + match *sd { + NASideData::Palette(_, ref pal) => { + for (dst, src) in dpal.chunks_mut(3).zip(pal.chunks(4)) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } +found_pal = true; + break; + }, + _ => {}, + }; + } +if !found_pal { + for i in 0..256 { + dpal[i * 3 + 0] = i as u8; + dpal[i * 3 + 1] = i as u8; + dpal[i * 3 + 2] = i as u8; + } +} + buftype = NABufferType::Video(buf); + } else { + let bufret = self.hams16.clone_ref(); + let mut buf; + if let Some(bbuf) = bufret { + buf = bbuf; + } else { + let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 2)?; + buf = bufinfo.get_vbuf16().unwrap(); + self.hams16.add_frame(buf); + buf = self.hams16.get_output_frame().unwrap(); + } + let mut frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap(); + ftype = self.decode_frame16(&mut br, &mut frm)?; + buftype = NABufferType::Video16(buf); + } + + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buftype); + frm.set_keyframe(ftype == FrameType::I); + frm.set_frame_type(ftype); + Ok(frm.into_ref()) + } + fn flush(&mut self) { + self.hams.clear(); + self.hams16.clear(); + } +} + +pub fn get_decoder() -> Box { + Box::new(Video1Decoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_codec_support::test::dec_video::*; + use crate::ms_register_all_codecs; + use nihav_commonfmt::generic_register_all_demuxers; + #[test] + fn test_ms_video1_8bit() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + ms_register_all_codecs(&mut dec_reg); + + test_decoding("avi", "msvideo1", "assets/MS/toon.avi", Some(66), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x0c26ec42, 0xb75bfea7, 0x1e6342ae, 0xb14dcfa3])); + } + #[test] + fn test_ms_video1_16bit() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + ms_register_all_codecs(&mut dec_reg); + + test_decoding("avi", "msvideo1", "assets/MS/clock-cram16.avi", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x03381fa4, 0x5b294fec, 0xb97a7575, 0xa1a3ffe9])); + } +} diff --git a/nihav-ms/src/lib.rs b/nihav-ms/src/lib.rs new file mode 100644 index 0000000..d543744 --- /dev/null +++ b/nihav-ms/src/lib.rs @@ -0,0 +1,5 @@ +extern crate nihav_core; +extern crate nihav_codec_support; + +mod codecs; +pub use crate::codecs::ms_register_all_codecs; diff --git a/nihav-registry/src/register.rs b/nihav-registry/src/register.rs index 1665b67..331748a 100644 --- a/nihav-registry/src/register.rs +++ b/nihav-registry/src/register.rs @@ -174,6 +174,10 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[ desc!(video; "cinepak", "Cinepak"), + desc!(video; "msvideo1", "MS Video 1"), + desc!(audio; "ms-adpcm", "MS ADPCM"), + desc!(audio; "ima-adpcm-ms", "IMA ADPCM (MS variant)"), + desc!(video; "truemotion1", "TrueMotion 1"), desc!(video-im; "truemotionrt", "TrueMotion RT"), desc!(video; "truemotion2", "TrueMotion 2"), @@ -222,6 +226,10 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[ ]; static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[ + (b"CRAM", "msvideo1"), + (b"MSVC", "msvideo1"), + (b"WHAM", "msvideo1"), + (b"IF09", "indeo1"), (b"RT21", "indeo2"), (b"IV31", "indeo3"), @@ -256,7 +264,9 @@ static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[ static WAV_CODEC_REGISTER: &'static [(u16, &str)] = &[ (0x0000, "pcm"), (0x0001, "pcm"), + (0x0002, "ms-adpcm"), (0x0003, "pcm"), + (0x0011, "ima-adpcm-ms"), (0x0061, "adpcm-dk4"), (0x0062, "adpcm-dk3"), (0x0401, "imc"),