From 606c448ef167017c3171b49f9eb2af82c15079ce Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Wed, 23 Jan 2019 14:04:05 +0100 Subject: [PATCH] RAD formats crate and Smacker demuxer and decoder --- nihav-allstuff/Cargo.toml | 1 + nihav-allstuff/src/lib.rs | 6 + nihav-core/src/detect.rs | 5 + nihav-rad/Cargo.toml | 28 ++ nihav-rad/src/codecs/bink2.rs | 6 + nihav-rad/src/codecs/binkaud.rs | 9 + nihav-rad/src/codecs/binkvid.rs | 6 + nihav-rad/src/codecs/mod.rs | 35 ++ nihav-rad/src/codecs/smacker.rs | 631 ++++++++++++++++++++++++++++++ nihav-rad/src/demuxers/bink.rs | 12 + nihav-rad/src/demuxers/mod.rs | 24 ++ nihav-rad/src/demuxers/smacker.rs | 319 +++++++++++++++ nihav-rad/src/lib.rs | 6 + 13 files changed, 1088 insertions(+) create mode 100644 nihav-rad/Cargo.toml create mode 100644 nihav-rad/src/codecs/bink2.rs create mode 100644 nihav-rad/src/codecs/binkaud.rs create mode 100644 nihav-rad/src/codecs/binkvid.rs create mode 100644 nihav-rad/src/codecs/mod.rs create mode 100644 nihav-rad/src/codecs/smacker.rs create mode 100644 nihav-rad/src/demuxers/bink.rs create mode 100644 nihav-rad/src/demuxers/mod.rs create mode 100644 nihav-rad/src/demuxers/smacker.rs create mode 100644 nihav-rad/src/lib.rs diff --git a/nihav-allstuff/Cargo.toml b/nihav-allstuff/Cargo.toml index 5003f75..152739c 100644 --- a/nihav-allstuff/Cargo.toml +++ b/nihav-allstuff/Cargo.toml @@ -9,4 +9,5 @@ nihav_core = { path = "../nihav-core" } nihav_commonfmt = { path = "../nihav-commonfmt" } nihav_game = { path = "../nihav-game" } nihav_indeo = { path = "../nihav-indeo" } +nihav_rad = { path = "../nihav-rad" } nihav_realmedia = { path = "../nihav-realmedia" } \ No newline at end of file diff --git a/nihav-allstuff/src/lib.rs b/nihav-allstuff/src/lib.rs index 667bcd0..88c5f45 100644 --- a/nihav-allstuff/src/lib.rs +++ b/nihav-allstuff/src/lib.rs @@ -2,6 +2,7 @@ extern crate nihav_core; extern crate nihav_commonfmt; extern crate nihav_game; extern crate nihav_indeo; +extern crate nihav_rad; extern crate nihav_realmedia; use nihav_core::codecs::RegisteredDecoders; @@ -15,6 +16,9 @@ use nihav_game::demuxers::game_register_all_demuxers; use nihav_indeo::codecs::indeo_register_all_codecs; +use nihav_rad::codecs::rad_register_all_codecs; +use nihav_rad::demuxers::rad_register_all_demuxers; + use nihav_realmedia::codecs::realmedia_register_all_codecs; use nihav_realmedia::demuxers::realmedia_register_all_demuxers; @@ -22,11 +26,13 @@ pub fn nihav_register_all_codecs(rd: &mut RegisteredDecoders) { generic_register_all_codecs(rd); game_register_all_codecs(rd); indeo_register_all_codecs(rd); + rad_register_all_codecs(rd); realmedia_register_all_codecs(rd); } pub fn nihav_register_all_demuxers(rd: &mut RegisteredDemuxers) { generic_register_all_demuxers(rd); game_register_all_demuxers(rd); + rad_register_all_demuxers(rd); realmedia_register_all_demuxers(rd); } diff --git a/nihav-core/src/detect.rs b/nihav-core/src/detect.rs index 0b60641..b2b3414 100644 --- a/nihav-core/src/detect.rs +++ b/nihav-core/src/detect.rs @@ -193,6 +193,11 @@ const DETECTORS: &[DetectConditions] = &[ extensions: ".ivr", conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b".R1M"), &CC::Str(b".REC"))}], }, + DetectConditions { + demux_name: "smacker", + extensions: ".smk", + conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b"SMK2"), &CC::Str(b"SMK4"))}], + }, ]; pub fn detect_format(name: &str, src: &mut ByteReader) -> Option<(&'static str, DetectionScore)> { diff --git a/nihav-rad/Cargo.toml b/nihav-rad/Cargo.toml new file mode 100644 index 0000000..e32dcd8 --- /dev/null +++ b/nihav-rad/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "nihav_rad" +version = "0.1.0" +authors = ["Kostya Shishkov "] +edition = "2018" + +[dependencies.nihav_core] +path = "../nihav-core" +features = ["fft"] + +[features] +default = ["all_decoders", "all_demuxers"] +demuxers = [] +all_demuxers = ["demuxer_smk", "demuxer_bink"] +demuxer_smk = ["demuxers"] +demuxer_bink = ["demuxers"] + +all_decoders = ["all_video_decoders", "all_audio_decoders"] +decoders = [] + +all_video_decoders = ["decoder_smkvid", "decoder_binkvid", "decoder_bink2"] +decoder_smkvid = ["decoders"] +decoder_binkvid = ["decoders"] +decoder_bink2 = ["decoders"] + +all_audio_decoders = ["decoder_smkaud", "decoder_binkaud"] +decoder_binkaud = ["decoders"] +decoder_smkaud = ["decoders"] \ No newline at end of file diff --git a/nihav-rad/src/codecs/bink2.rs b/nihav-rad/src/codecs/bink2.rs new file mode 100644 index 0000000..2777e5f --- /dev/null +++ b/nihav-rad/src/codecs/bink2.rs @@ -0,0 +1,6 @@ +use nihav_core::codecs::*; + +pub fn get_decoder() -> Box { +unimplemented!(""); +} + diff --git a/nihav-rad/src/codecs/binkaud.rs b/nihav-rad/src/codecs/binkaud.rs new file mode 100644 index 0000000..ba8f707 --- /dev/null +++ b/nihav-rad/src/codecs/binkaud.rs @@ -0,0 +1,9 @@ +use nihav_core::codecs::*; + +pub fn get_decoder_dct() -> Box { +unimplemented!(""); +} + +pub fn get_decoder_rdft() -> Box { +unimplemented!(""); +} diff --git a/nihav-rad/src/codecs/binkvid.rs b/nihav-rad/src/codecs/binkvid.rs new file mode 100644 index 0000000..2777e5f --- /dev/null +++ b/nihav-rad/src/codecs/binkvid.rs @@ -0,0 +1,6 @@ +use nihav_core::codecs::*; + +pub fn get_decoder() -> Box { +unimplemented!(""); +} + diff --git a/nihav-rad/src/codecs/mod.rs b/nihav-rad/src/codecs/mod.rs new file mode 100644 index 0000000..4ce5563 --- /dev/null +++ b/nihav-rad/src/codecs/mod.rs @@ -0,0 +1,35 @@ +use nihav_core::codecs::*; + +macro_rules! validate { + ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DecoderError::InvalidData); } }; +} + +#[cfg(any(feature="decoder_smkaud", feature="decoder_smkvid"))] +mod smacker; +#[cfg(feature="decoder_binkaud")] +mod binkaud; +#[cfg(feature="decoder_binkvid")] +mod binkvid; +#[cfg(feature="decoder_bink2")] +mod bink2; + +const RAD_CODECS: &[DecoderInfo] = &[ +#[cfg(feature="decoder_smkaud")] + DecoderInfo { name: "smacker-audio", get_decoder: smacker::get_decoder_audio }, +#[cfg(feature="decoder_smkvid")] + DecoderInfo { name: "smacker-video", get_decoder: smacker::get_decoder_video }, +#[cfg(feature="decoder_binkaud")] + DecoderInfo { name: "bink-audio-dct", get_decoder: binkaud::get_decoder_dct }, +#[cfg(feature="decoder_binkaud")] + DecoderInfo { name: "bink-audio-rdft", get_decoder: binkaud::get_decoder_rdft }, +#[cfg(feature="decoder_binkvid")] + DecoderInfo { name: "bink-video", get_decoder: binkvid::get_decoder }, +#[cfg(feature="decoder_bink2")] + DecoderInfo { name: "bink2-video", get_decoder: bink2::get_decoder }, +]; + +pub fn rad_register_all_codecs(rd: &mut RegisteredDecoders) { + for decoder in RAD_CODECS.into_iter() { + rd.add_decoder(decoder.clone()); + } +} diff --git a/nihav-rad/src/codecs/smacker.rs b/nihav-rad/src/codecs/smacker.rs new file mode 100644 index 0000000..e6f4268 --- /dev/null +++ b/nihav-rad/src/codecs/smacker.rs @@ -0,0 +1,631 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; +use nihav_core::io::bitreader::*; +use nihav_core::io::codebook::*; +use std::str::FromStr; + +const PAL_SIZE: usize = 768; +const SMK_FLAG_INTERLACED: u32 = 0x02; +const SMK_FLAG_SCALED: u32 = 0x04; + +struct SmackerTree8 { + cb: Option>, + defsym: u8, +} + +fn get_tree8(br: &mut BitReader, bits: &mut [u8; 256], codes: &mut [u32; 256], syms: &mut [u8; 256], count: &mut usize, len: u8, prefix: u32) -> DecoderResult<()> { + if !br.read_bool()? { + bits[*count] = len; + codes[*count] = prefix; + syms[*count] = br.read(8)? as u8; + *count += 1; + } else { + validate!((*count <= 256 - 2) && (len <= 31)); + get_tree8(br, bits, codes, syms, count, len + 1, prefix)?; + get_tree8(br, bits, codes, syms, count, len + 1, prefix | (1 << len))?; + } + Ok(()) +} + +pub struct FullTableCodebookDescReader<'a> { + bits: &'a [u8], + codes: &'a [u32], + syms: &'a [u8], +} + +impl<'a> FullTableCodebookDescReader<'a> { + pub fn new(codes: &'a [u32], bits: &'a [u8], syms: &'a [u8]) -> Self { + Self { bits, codes, syms } + } +} +impl<'a> CodebookDescReader for FullTableCodebookDescReader<'a> +{ + fn bits(&mut self, idx: usize) -> u8 { self.bits[idx] } + fn code(&mut self, idx: usize) -> u32 { self.codes[idx] } + fn sym (&mut self, idx: usize) -> u8 { self.syms[idx] } + fn len(&mut self) -> usize { self.bits.len() } +} + +impl SmackerTree8 { + fn new() -> Self { + Self { + cb: None, + defsym: 0, + } + } + fn decode(&mut self, br: &mut BitReader) -> DecoderResult<()> { + if !br.read_bool()? { return Ok(()); } + + let mut bits: [u8; 256] = [0; 256]; + let mut codes: [u32; 256] = [0; 256]; + let mut syms: [u8; 256] = [0; 256]; + let mut count = 0; + + get_tree8(br, &mut bits, &mut codes, &mut syms, &mut count, 0, 0)?; + validate!(!br.read_bool()?); + + if count > 1 { + let mut cr = FullTableCodebookDescReader::new(&codes[0..count], &bits[0..count], &syms[0..count]); + let cb = Codebook::new(&mut cr, CodebookMode::LSB)?; + self.cb = Some(cb); + } else { + self.defsym = syms[0]; + } + + Ok(()) + } +} + +struct SmackerTree16 { + tree: Vec, + last: [usize; 3], +} + +struct SmackerTree16Builder { + tree_lo: SmackerTree8, + tree_hi: SmackerTree8, + nsyms: usize, + esc: [u32; 3], +} + +const SMK_BIGTREE_NODE: u32 = 0x80000000; +const SMK_LAST_UNINIT: usize = 0xFFFFFFFF; + +impl SmackerTree16Builder { + fn get_tree16(&mut self, br: &mut BitReader, tree: &mut SmackerTree16, depth: usize) -> DecoderResult { + validate!(tree.tree.len() < self.nsyms); + if !br.read_bool()? { + let lo = br.read_tree8(&self.tree_lo)?; + let hi = br.read_tree8(&self.tree_hi)?; + let mut sym = (((hi as u16) << 8) | (lo as u16)) as u32; + if sym == self.esc[0] { + tree.last[0] = tree.tree.len(); + sym = 0; + } else if sym == self.esc[1] { + tree.last[1] = tree.tree.len(); + sym = 0; + } else if sym == self.esc[2] { + tree.last[2] = tree.tree.len(); + sym = 0; + } + tree.tree.push(sym); + Ok(1) + } else { + let cur_idx = tree.tree.len(); + tree.tree.push(0); + let lcount = self.get_tree16(br, tree, depth + 1)?; + let rcount = self.get_tree16(br, tree, depth + 1)?; + tree.tree[cur_idx] = SMK_BIGTREE_NODE | lcount; + Ok(lcount + rcount + 1) + } + } +} + +impl SmackerTree16 { + fn new() -> Self { + Self { + tree: Vec::new(), + last: [SMK_LAST_UNINIT; 3], + } + } + fn decode(&mut self, br: &mut BitReader, size: u32) -> DecoderResult<()> { + if !br.read_bool()? { return Ok(()); } + + let mut tree_lo = SmackerTree8::new(); + tree_lo.decode(br)?; + let mut tree_hi = SmackerTree8::new(); + tree_hi.decode(br)?; + + let mut esc: [u32; 3] = [0; 3]; + for i in 0..esc.len() { + esc[i] = br.read(16)? as u32; + } + + let nsyms = (((size + 3) >> 2) + 4) as usize; + self.tree = Vec::with_capacity(nsyms); + + let mut tb = SmackerTree16Builder { tree_lo, tree_hi, nsyms, esc }; + + tb.get_tree16(br, self, 0)?; + validate!(!br.read_bool()?); + + for i in 0..self.last.len() { + if self.last[i] == SMK_LAST_UNINIT { + self.last[i] = self.tree.len(); + self.tree.push(0); + } + } + validate!(self.tree.len() <= nsyms); + Ok(()) + } + fn reset(&mut self) { + for i in 0..self.last.len() { + self.tree[self.last[i]] = 0; + } + } +} + +trait ReadTree { + fn read_tree8(&mut self, tree: &SmackerTree8) -> DecoderResult; + fn read_bigtree(&mut self, tree: &mut SmackerTree16) -> DecoderResult; +} + +impl<'a> ReadTree for BitReader<'a> { + fn read_tree8(&mut self, tree: &SmackerTree8) -> DecoderResult { + if let Some(ref cb) = tree.cb { + Ok(self.read_cb(cb)?) + } else { + Ok(tree.defsym) + } + } + fn read_bigtree(&mut self, tree: &mut SmackerTree16) -> DecoderResult { + let mut pos = 0; + while (tree.tree[pos] & SMK_BIGTREE_NODE) != 0 { + if self.read_bool()? { + pos += (tree.tree[pos] & !SMK_BIGTREE_NODE) as usize; + } + pos += 1; + } + let val = tree.tree[pos]; + if val != tree.tree[tree.last[0]] { + tree.tree[tree.last[2]] = tree.tree[tree.last[1]]; + tree.tree[tree.last[1]] = tree.tree[tree.last[0]]; + tree.tree[tree.last[0]] = val; + } + Ok(val as u16) + } +} + +const SMK_BLOCK_RUNS: [usize; 64] = [ + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 128, 256, 512, 1024, 2048 +]; + +struct SmackerVideoDecoder { + info: Rc, + mmap_tree: SmackerTree16, + mclr_tree: SmackerTree16, + full_tree: SmackerTree16, + type_tree: SmackerTree16, + w: usize, + h: usize, + bw: usize, + bh: usize, + is_ver4: bool, + flags: u32, + frame: Vec, + stride: usize, +} + +impl SmackerVideoDecoder { + fn new() -> Self { + let dummy_info = Rc::new(DUMMY_CODEC_INFO); + Self { + info: dummy_info, + mmap_tree: SmackerTree16::new(), + mclr_tree: SmackerTree16::new(), + full_tree: SmackerTree16::new(), + type_tree: SmackerTree16::new(), + w: 0, + h: 0, + bw: 0, + bh: 0, + is_ver4: false, + flags: 0, + frame: Vec::new(), + stride: 0, + } + } + fn block_pos(&self, blk_no: usize) -> usize { + let bx = blk_no % self.bw; + let by = blk_no / self.bw; + bx * 4 + by * 4 * self.stride + } + fn decode_frame(&mut self, br: &mut BitReader) -> DecoderResult { + let mut is_intra = true; + let blocks = self.bw * self.bh; + + self.mmap_tree.reset(); + self.mclr_tree.reset(); + self.full_tree.reset(); + self.type_tree.reset(); + + let mut block = 0; + while block < blocks { + let btype = br.read_bigtree(&mut self.type_tree)?; + let run = SMK_BLOCK_RUNS[((btype as usize) >> 2) & 0x3F]; + validate!(run <= blocks - block); + match btype & 3 { + 0 => { // two-colour pattern + for i in 0..run { + let clr = br.read_bigtree(&mut self.mclr_tree)?; + let mut map = br.read_bigtree(&mut self.mmap_tree)?; + let hi = (clr >> 8) as u8; + let lo = (clr & 0xFF) as u8; + let mut doff = self.block_pos(block + i); + for _ in 0..4 { + self.frame[doff + 0] = if (map & 1) != 0 { hi } else { lo }; + self.frame[doff + 1] = if (map & 2) != 0 { hi } else { lo }; + self.frame[doff + 2] = if (map & 4) != 0 { hi } else { lo }; + self.frame[doff + 3] = if (map & 8) != 0 { hi } else { lo }; + map >>= 4; + doff += self.stride; + } + } + }, + 1 => { // full + let mode; + if !self.is_ver4 || !br.read_bool()? { + mode = 0; + } else { + mode = 1 + br.read(1)?; + } + for i in 0..run { + let mut doff = self.block_pos(block + i); + match mode { + 0 => { + for _ in 0..4 { + let clr0 = br.read_bigtree(&mut self.full_tree)?; + let clr1 = br.read_bigtree(&mut self.full_tree)?; + self.frame[doff + 0] = (clr1 & 0xFF) as u8; + self.frame[doff + 1] = (clr1 >> 8) as u8; + self.frame[doff + 2] = (clr0 & 0xFF) as u8; + self.frame[doff + 3] = (clr0 >> 8) as u8; + doff += self.stride; + } + }, + 1 => { + for _ in 0..2 { + let clr = br.read_bigtree(&mut self.full_tree)?; + self.frame[doff + 0] = (clr & 0xFF) as u8; + self.frame[doff + 1] = (clr & 0xFF) as u8; + self.frame[doff + 2] = (clr >> 8) as u8; + self.frame[doff + 3] = (clr >> 8) as u8; + doff += self.stride; + self.frame[doff + 0] = (clr & 0xFF) as u8; + self.frame[doff + 1] = (clr & 0xFF) as u8; + self.frame[doff + 2] = (clr >> 8) as u8; + self.frame[doff + 3] = (clr >> 8) as u8; + doff += self.stride; + } + }, + 2 => { + for _ in 0..2 { + let clr0 = br.read_bigtree(&mut self.full_tree)?; + let clr1 = br.read_bigtree(&mut self.full_tree)?; + self.frame[doff + 0] = (clr1 & 0xFF) as u8; + self.frame[doff + 1] = (clr1 >> 8) as u8; + self.frame[doff + 2] = (clr0 & 0xFF) as u8; + self.frame[doff + 3] = (clr0 >> 8) as u8; + doff += self.stride; + self.frame[doff + 0] = (clr1 & 0xFF) as u8; + self.frame[doff + 1] = (clr1 >> 8) as u8; + self.frame[doff + 2] = (clr0 & 0xFF) as u8; + self.frame[doff + 3] = (clr0 >> 8) as u8; + doff += self.stride; + } + }, + _ => unreachable!(), + }; + } + }, + 2 => { // skip + is_intra = false; + }, + 3 => { // fill + let clr = (btype >> 8) as u8; + for i in 0..run { + let mut doff = self.block_pos(block + i); + for _ in 0..4 { + self.frame[doff + 0] = clr; + self.frame[doff + 1] = clr; + self.frame[doff + 2] = clr; + self.frame[doff + 3] = clr; + doff += self.stride; + } + } + }, + _ => unreachable!(), + }; + block += run; + } + Ok(is_intra) + } + fn output_frame(&self, buf: &mut NAVideoBuffer) { + let stride = buf.get_stride(0); + let mut data = buf.get_data_mut(); + let dst = data.as_mut_slice(); + let is_scaled = (self.flags & SMK_FLAG_SCALED) != 0; + let is_interlaced = (self.flags & SMK_FLAG_INTERLACED) != 0; + let mut didx = 0; + let mut sidx = 0; + for _ in 0..self.h { + for x in 0..self.w { dst[didx + x] = self.frame[sidx + x]; } + sidx += self.stride; + didx += stride; + if is_scaled { + for x in 0..self.w { dst[didx + x] = dst[didx - stride + x]; } + didx += stride; + } + if is_interlaced { + for x in 0..self.w { dst[didx + x] = 0; } + didx += stride; + if is_scaled { + for x in 0..self.w { dst[didx + x] = 0; } + didx += stride; + } + } + } + } +} + +impl NADecoder for SmackerVideoDecoder { + fn init(&mut self, info: Rc) -> DecoderResult<()> { + if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { + let w = vinfo.get_width(); + let h = vinfo.get_height(); + let fmt = PAL8_FORMAT; + + self.w = w; + self.h = h; + self.bw = w >> 2; + self.bh = h >> 2; + let edata = info.get_extradata().unwrap(); + validate!(edata.len() > 24); + + self.stride = w; + self.frame.resize(w * h, 0); + + let mut mr = MemoryReader::new_read(&edata); + let mut br = ByteReader::new(&mut mr); + let magic = br.read_u32be()?; + self.flags = br.read_u32le()?; + let mmap_size = br.read_u32le()?; + let mclr_size = br.read_u32le()?; + let full_size = br.read_u32le()?; + let type_size = br.read_u32le()?; + + self.is_ver4 = (magic & 0xFF) == 0x34; + let mut br = BitReader::new(&edata[24..], edata.len() - 24, BitReaderMode::LE); + self.mmap_tree.decode(&mut br, mmap_size)?; + self.mclr_tree.decode(&mut br, mclr_size)?; + self.full_tree.decode(&mut br, full_size)?; + self.type_tree.decode(&mut br, type_size)?; + + let mut out_h = h; + if (self.flags & SMK_FLAG_INTERLACED) != 0 { + out_h <<= 1; + } + if (self.flags & SMK_FLAG_SCALED) != 0 { + out_h <<= 1; + } + let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, out_h, false, fmt)); + self.info = Rc::new(NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata())); + + + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, pkt: &NAPacket) -> DecoderResult { + let src = pkt.get_buffer(); + validate!(src.len() >= PAL_SIZE); + + let is_intra; + let ftype; + let mut bufinfo; + if src.len() > PAL_SIZE { + let mut br = BitReader::new(&src[PAL_SIZE..], src.len() - PAL_SIZE, BitReaderMode::LE); + + let bufret = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 2); + if let Err(_) = bufret { return Err(DecoderError::InvalidData); } + bufinfo = bufret.unwrap(); + let mut buf = bufinfo.get_vbuf().unwrap(); + is_intra = self.decode_frame(&mut br)?; + self.output_frame(&mut buf); + let paloff = buf.get_offset(1); + let mut data = buf.get_data_mut(); + let dst = data.as_mut_slice(); + let palout = &mut dst[paloff..][..PAL_SIZE]; + palout.copy_from_slice(&src[0..PAL_SIZE]); + ftype = if is_intra { FrameType::I } else { FrameType::P }; + } else { + bufinfo = NABufferType::None; + ftype = FrameType::Skip; + is_intra = false; + } + + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); + frm.set_keyframe(is_intra); + frm.set_frame_type(ftype); + Ok(Rc::new(RefCell::new(frm))) + } +} + +pub fn get_decoder_video() -> Box { + Box::new(SmackerVideoDecoder::new()) +} + +struct SmackerAudioDecoder { + ainfo: NAAudioInfo, + chmap: NAChannelMap, + chans: usize, + bits: u8, +} + +impl SmackerAudioDecoder { + fn new() -> Self { + Self { + ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0), + chmap: NAChannelMap::new(), + chans: 0, + bits: 0, + } + } +} + +impl NADecoder for SmackerAudioDecoder { + fn init(&mut self, info: Rc) -> DecoderResult<()> { + if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { + self.bits = ainfo.get_format().get_bits(); + let fmt = if self.bits == 8 { SND_U8_FORMAT } else { SND_S16P_FORMAT }; + self.chans = ainfo.get_channels() as usize; + self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), ainfo.get_channels(), fmt, 0); + self.chmap = NAChannelMap::from_str(if ainfo.get_channels() == 2 {"L,R"} else {"C"}).unwrap(); + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, pkt: &NAPacket) -> DecoderResult { + let info = pkt.get_stream().get_info(); + if let NACodecTypeInfo::Audio(_) = info.get_properties() { + let src = pkt.get_buffer(); + validate!(src.len() > 4); + let mut br = BitReader::new(&src, src.len(), BitReaderMode::LE); + let unp_size = br.read(32)? as usize; + if !br.read_bool()? { + let mut frm = NAFrame::new_from_pkt(pkt, info.clone(), NABufferType::None); + frm.set_frame_type(FrameType::Skip); + return Ok(Rc::new(RefCell::new(frm))); + } + let stereo = br.read_bool()?; + let bits16 = br.read_bool()?; + validate!(!(stereo ^ (self.chans == 2))); + validate!(!(bits16 ^ (self.bits == 16))); + + let mut abuf; + let samples; + let nch = if stereo { 2 } else { 1 }; + if bits16 { + samples = unp_size / 2 / nch; + let mask = if stereo { 1 } else { 0 }; + let mut trees: [SmackerTree8; 4] = [SmackerTree8::new(), SmackerTree8::new(), SmackerTree8::new(), SmackerTree8::new()]; + for i in 0..nch*2 { + trees[i].decode(&mut br)?; + } + let mut pred: [i16; 2] = [0; 2]; + for i in 0..nch { + let hi = br.read(8)?; + let lo = br.read(8)?; + pred[nch - i - 1] = (lo | (hi << 8)) as i16; + } + + abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?; + let mut adata = abuf.get_abuf_i16().unwrap(); + let offs: [usize; 2] = [0, adata.get_offset(1)]; + let mut dst = adata.get_data_mut(); + for ch in 0..nch { + dst[offs[ch]] = pred[ch]; + } + for i in nch..(unp_size >> 1) { + let idx = i & mask; + let lo = br.read_tree8(&trees[idx * 2 + 0])? as u16; + let hi = br.read_tree8(&trees[idx * 2 + 1])? as u16; + let diff = (lo | (hi << 8)) as i16; + pred[idx] = pred[idx].wrapping_add(diff); + dst[offs[idx] + (i >> 1)] = pred[idx]; + } + } else { + samples = unp_size / nch; + abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?; + let mut adata = abuf.get_abuf_u8().unwrap(); + let mut dst = adata.get_data_mut(); + if stereo { + let mut trees: [SmackerTree8; 2] = [SmackerTree8::new(), SmackerTree8::new()]; + trees[0].decode(&mut br)?; + trees[1].decode(&mut br)?; + let pred0 = br.read(8)? as u8; + let pred1 = br.read(8)? as u8; + let mut pred: [u8; 2] = [ pred1, pred0 ]; + for ch in 0..2 { dst[ch] = pred[ch]; } + for i in 2..unp_size { + let diff = br.read_tree8(&trees[i & 1])? as u8; + pred[i & 1] = pred[i & 1].wrapping_add(diff); + dst[i] = pred[i & 1]; + } + } else { + let mut tree = SmackerTree8::new(); + tree.decode(&mut br)?; + let mut pred = br.read(8)? as u8; + dst[0] = pred; + for i in 1..unp_size { + let diff = br.read_tree8(&tree)? as u8; + pred = pred.wrapping_add(diff); + dst[i] = pred; + } + } + } + let mut frm = NAFrame::new_from_pkt(pkt, info, abuf); + frm.set_duration(Some(samples as u64)); + frm.set_keyframe(false); + Ok(Rc::new(RefCell::new(frm))) + } else { + Err(DecoderError::InvalidData) + } + } +} + +pub fn get_decoder_audio() -> Box { + Box::new(SmackerAudioDecoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_core::test::dec_video::*; + use crate::codecs::rad_register_all_codecs; + use crate::demuxers::rad_register_all_demuxers; + #[test] + fn test_smkvid() { + let mut dmx_reg = RegisteredDemuxers::new(); + rad_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + rad_register_all_codecs(&mut dec_reg); + + //let file = "assets/RAD/20130507_audio-distortion.smk"; + //let file = "assets/RAD/ajfstr1.smk"; + //let file = "assets/RAD/credits.smk"; + let file = "assets/RAD/wetlogo.smk"; + test_file_decoding("smacker", file, Some(1000), true, false, None, &dmx_reg, &dec_reg); + } + #[test] + fn test_smkaud() { + let mut dmx_reg = RegisteredDemuxers::new(); + rad_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + rad_register_all_codecs(&mut dec_reg); + + //let file = "assets/RAD/20130507_audio-distortion.smk"; + let file = "assets/RAD/wetlogo.smk"; + test_decode_audio("smacker", file, None, "smk", &dmx_reg, &dec_reg); + } +} diff --git a/nihav-rad/src/demuxers/bink.rs b/nihav-rad/src/demuxers/bink.rs new file mode 100644 index 0000000..879efca --- /dev/null +++ b/nihav-rad/src/demuxers/bink.rs @@ -0,0 +1,12 @@ +use nihav_core::demuxers::*; +use nihav_core::io::byteio::ByteReader; + +pub struct BinkDemuxerCreator { } + +impl DemuxerCreator for BinkDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box + 'a> { + unimplemented!("");//Box::new(GremlinVideoDemuxer::new(br)) + } + fn get_name(&self) -> &'static str { "bink" } +} + diff --git a/nihav-rad/src/demuxers/mod.rs b/nihav-rad/src/demuxers/mod.rs new file mode 100644 index 0000000..a1234bd --- /dev/null +++ b/nihav-rad/src/demuxers/mod.rs @@ -0,0 +1,24 @@ +use nihav_core::demuxers::*; + +#[allow(unused_macros)] +macro_rules! validate { + ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DemuxerError::InvalidData); } }; +} + +#[cfg(feature="demuxer_smk")] +mod smacker; +#[cfg(feature="demuxer_bink")] +mod bink; + +const RAD_DEMUXERS: &[&'static DemuxerCreator] = &[ +#[cfg(feature="demuxer_smk")] + &smacker::SMKDemuxerCreator {}, +#[cfg(feature="demuxer_bink")] + &bink::BinkDemuxerCreator {}, +]; + +pub fn rad_register_all_demuxers(rd: &mut RegisteredDemuxers) { + for demuxer in RAD_DEMUXERS.into_iter() { + rd.add_demuxer(*demuxer); + } +} diff --git a/nihav-rad/src/demuxers/smacker.rs b/nihav-rad/src/demuxers/smacker.rs new file mode 100644 index 0000000..b666cf1 --- /dev/null +++ b/nihav-rad/src/demuxers/smacker.rs @@ -0,0 +1,319 @@ +use std::io::SeekFrom; +use nihav_core::demuxers::*; + +const SMK_FLAG_LOOP_FRAME: u32 = 0x01; + +const SMK_FRAME_FLAG_PALETTE: u8 = 0x01; + +const SMK_AUD_FLAG_PACKED: u8 = 0x80; +const SMK_AUD_FLAG_PRESENT: u8 = 0x40; +const SMK_AUD_FLAG_16BIT: u8 = 0x20; +const SMK_AUD_FLAG_STEREO: u8 = 0x10; +const SMK_AUD_FLAG_BINKAUD_RDFT: u8 = 0x08; +const SMK_AUD_FLAG_BINKAUD_DCT: u8 = 0x04; + +const NUM_AUDIO_TRACKS: usize = 7; + +const PAL_SIZE: usize = 768; + +const SMK_DEFAULT_PAL: [u8; 64] = [ + 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, + 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, + 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, + 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, + 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, + 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, + 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, + 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF +]; + +#[derive(Clone,Copy)] +struct AudioTrack { + size: u32, + flags: u8, + srate: u32, + id: usize, +} + +impl AudioTrack { + fn new() -> Self { + Self { size: 0, flags: 0, srate: 0, id: 0 } + } + fn is_present(&self) -> bool { + (self.flags & SMK_AUD_FLAG_PRESENT) != 0 + } + fn add_stream(&mut self, strmgr: &mut StreamManager, str_id: usize) -> DemuxerResult<()> { + let channels = if (self.flags & SMK_AUD_FLAG_STEREO) != 0 { 2 } else { 1 }; + let codecname = if (self.flags & SMK_AUD_FLAG_BINKAUD_RDFT) != 0 { + "bink-audio-rdft" + } else if (self.flags & SMK_AUD_FLAG_BINKAUD_DCT) != 0 { + "bink-audio-dct" + } else if (self.flags & SMK_AUD_FLAG_PACKED) != 0 { + "smacker-audio" + } else { + "pcm" + }; + let soniton = if (self.flags & SMK_AUD_FLAG_16BIT) != 0 { SND_S16_FORMAT } else { SND_U8_FORMAT }; + let ahdr = NAAudioInfo::new(self.srate, channels, soniton, 1); + let ainfo = NACodecInfo::new(codecname, NACodecTypeInfo::Audio(ahdr), None); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, (str_id + 1) as u32, ainfo, 1, self.srate)); + validate!(res.is_some()); + self.id = res.unwrap(); + + Ok(()) + } +} + +struct SmackerVideoDemuxer<'a> { + src: &'a mut ByteReader<'a>, + frames: usize, + pts_inc: u64, + cur_pts: u64, + ainfo: [AudioTrack; NUM_AUDIO_TRACKS], + frame_sizes: Vec, + frame_flags: Vec, + video_id: usize, + start: u64, + cur_frame: usize, + queued_packets: Vec, + pal: [u8; PAL_SIZE], +} + +/*macro_rules! mktag { + ($a:expr, $b:expr, $c:expr, $d:expr) => ({ + (($a as u32) << 24) | (($b as u32) << 16) | (($c as u32) << 8) | ($d as u32) + }); + ($arr:expr) => ({ + (($arr[0] as u32) << 24) | (($arr[1] as u32) << 16) | (($arr[2] as u32) << 8) | ($arr[3] as u32) + }); +}*/ + +fn get_pts_inc(val: i32) -> u64 { + if val > 0 { (val as u64) * 100 } + else if val < 0 { (-val as u64) } + else { 1 } +} + +impl<'a> DemuxCore<'a> for SmackerVideoDemuxer<'a> { + fn open(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> { + let src = &mut self.src; + let mut magic: [u8; 4] = [0; 4]; + src.read_buf(&mut magic)?; + validate!((&magic == b"SMK2") || (&magic == b"SMK4")); + let width = src.read_u32le()? as usize; + let height = src.read_u32le()? as usize; + self.frames = src.read_u32le()? as usize; + let pts_inc = src.read_u32le()? as i32; + let flags = src.read_u32le()?; + validate!((width > 0) && (height > 0) && (self.frames > 0)); + self.pts_inc = get_pts_inc(pts_inc); + + if (flags & SMK_FLAG_LOOP_FRAME) != 0 { + self.frames += 1; + } + + for i in 0..NUM_AUDIO_TRACKS { + self.ainfo[i].size = src.read_u32le()?; + } + let treesize = src.read_u32le()? as usize; + + let mut treedata: Vec = Vec::with_capacity(treesize + 20); + treedata.resize(treesize + 24, 0); + let hdr = &mut treedata[0..4]; + hdr.copy_from_slice(&magic); + let flg = &mut treedata[4..8]; + flg[0] = (flags & 0xFF) as u8; + flg[1] = (flags >> 8) as u8; + flg[2] = (flags >> 16) as u8; + flg[3] = (flags >> 24) as u8; + src.read_buf(&mut treedata[8..][..16])?; + + for i in 0..NUM_AUDIO_TRACKS { + self.ainfo[i].srate = src.read_u24le()?; + self.ainfo[i].flags = src.read_byte()?; + } + + src.read_skip(4)?; + + self.frame_sizes.resize(self.frames, 0); + self.frame_flags.resize(self.frames, 0); + for i in 0..self.frames { + self.frame_sizes[i] = src.read_u32le()?; + } + for i in 0..self.frames { + self.frame_flags[i] = src.read_byte()?; + } + + src.read_buf(&mut treedata[24..])?; + + let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT); + let vinfo = NACodecInfo::new("smacker-video", NACodecTypeInfo::Video(vhdr), Some(treedata)); + let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 100000)); + validate!(res.is_some()); + self.video_id = res.unwrap(); + + for i in 0..NUM_AUDIO_TRACKS { + if self.ainfo[i].is_present() { + self.ainfo[i].add_stream(strmgr, i)?; + } + } + + self.start = src.tell(); + self.cur_frame = 0; + self.reset_state(); + + Ok(()) + } + fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult { + if !self.queued_packets.is_empty() { + let pkt = self.queued_packets.pop().unwrap(); + return Ok(pkt); + } + if self.cur_frame >= self.frames { return Err(DemuxerError::EOF); } + + let mut payload_size = (self.frame_sizes[self.cur_frame] & !3) as usize; + let frame_flags = self.frame_flags[self.cur_frame]; + if (frame_flags & SMK_FRAME_FLAG_PALETTE) != 0 { + let chunk_size = (self.src.read_byte()? as usize) * 4; + validate!(chunk_size > 0); + validate!(payload_size >= chunk_size); + payload_size -= chunk_size; + let oldpal = self.pal.clone(); + let mut idx = 0; + let endpos = self.src.tell() + (chunk_size as u64) - 1; + while idx < 256 { + let op = self.src.read_byte()?; + if (op & 0x80) != 0 { + idx += ((op & 0x7F) as usize) + 1; + } else if (op & 0x40) != 0 { + let start = self.src.read_byte()? as usize; + let len = ((op & 0x3F) as usize) + 1; + validate!(idx + len <= 256); + for i in 0..len*3 { + self.pal[idx * 3 + i] = oldpal[start * 3 + i]; + } + idx += len; + } else { + let ix0 = op as usize; + let ix1 = (self.src.read_byte()? & 0x3F) as usize; + let ix2 = (self.src.read_byte()? & 0x3F) as usize; + self.pal[idx * 3 + 0] = SMK_DEFAULT_PAL[ix0]; + self.pal[idx * 3 + 1] = SMK_DEFAULT_PAL[ix1]; + self.pal[idx * 3 + 2] = SMK_DEFAULT_PAL[ix2]; + idx += 1; + } + } + validate!(self.src.tell() <= endpos); + if self.src.tell() < endpos { + let skip_size = endpos - self.src.tell(); + self.src.read_skip(skip_size as usize)?; + } + } + + let ts = NATimeInfo::new(Some(self.cur_pts), None, None, 1, 100000); + for i in 0..NUM_AUDIO_TRACKS { + if ((frame_flags >> (i + 1)) & 1) == 0 { continue; } + validate!(self.ainfo[i].is_present()); + let size = self.src.read_u32le()? as usize; + validate!(size > 4); + validate!(payload_size >= size); + payload_size -= size; + let strres = strmgr.get_stream(self.ainfo[i].id); + validate!(strres.is_some()); + let stream = strres.unwrap(); + let pkt = self.src.read_packet(stream.clone(), ts, true, size - 4)?; + self.queued_packets.push(pkt); + } + self.queued_packets.reverse(); + + let mut buf: Vec = Vec::with_capacity(PAL_SIZE + payload_size); + buf.resize(PAL_SIZE, 0); + buf.copy_from_slice(&self.pal[0..]); + if payload_size > 0 { + buf.resize(PAL_SIZE + payload_size, 0); + self.src.read_buf(&mut buf[PAL_SIZE..])?; + } + + let strres = strmgr.get_stream(self.video_id); + validate!(strres.is_some()); + let stream = strres.unwrap(); + let keyframe = (self.frame_sizes[self.cur_frame] & 1) != 0; + let pkt = NAPacket::new(stream, ts, keyframe, buf); + + self.cur_frame += 1; + self.cur_pts += self.pts_inc; + + Ok(pkt) + } + #[allow(unused_variables)] + fn seek(&mut self, time: u64) -> DemuxerResult<()> { + if time == 0 { + let start = self.start; + self.src.seek(SeekFrom::Start(start))?; + self.cur_frame = 0; + self.cur_pts = 0; + self.reset_state(); + return Ok(()); + } + Err(DemuxerError::NotImplemented) + } +} + +impl<'a> SmackerVideoDemuxer<'a> { + fn new(io: &'a mut ByteReader<'a>) -> Self { + SmackerVideoDemuxer { + src: io, + frames: 0, + pts_inc: 0, + cur_pts: 0, + ainfo: [AudioTrack::new(); NUM_AUDIO_TRACKS], + frame_sizes: Vec::new(), + frame_flags: Vec::new(), + video_id: 0, + start: 0, + cur_frame: 0, + queued_packets: Vec::new(), + pal: [0; PAL_SIZE], + } + } + fn reset_state(&mut self) { + self.queued_packets.truncate(0); + } +} + +pub struct SMKDemuxerCreator { } + +impl DemuxerCreator for SMKDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box + 'a> { + Box::new(SmackerVideoDemuxer::new(br)) + } + fn get_name(&self) -> &'static str { "smacker" } +} + +#[cfg(test)] +mod test { + use super::*; + use std::fs::File; + + #[test] + fn test_smk_demux() { + let mut file = File::open("assets/RAD/20130507_audio-distortion.smk").unwrap(); +// let mut file = File::open("assets/RAD/ajfstr1.smk").unwrap(); +// let mut file = File::open("assets/RAD/credits.smk").unwrap(); +// let mut file = File::open("assets/RAD/wetlogo.smk").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = SmackerVideoDemuxer::new(&mut br); + let mut sm = StreamManager::new(); + dmx.open(&mut sm).unwrap(); + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if (e as i32) == (DemuxerError::EOF as i32) { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } +} diff --git a/nihav-rad/src/lib.rs b/nihav-rad/src/lib.rs new file mode 100644 index 0000000..f723e9e --- /dev/null +++ b/nihav-rad/src/lib.rs @@ -0,0 +1,6 @@ +extern crate nihav_core; + +#[cfg(feature="decoders")] +pub mod codecs; +#[cfg(feature="demuxers")] +pub mod demuxers; \ No newline at end of file -- 2.39.5