aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKostya Shishkov <kostya.shishkov@gmail.com>2019-01-23 14:04:05 +0100
committerKostya Shishkov <kostya.shishkov@gmail.com>2019-01-23 14:04:05 +0100
commit606c448ef167017c3171b49f9eb2af82c15079ce (patch)
tree7012c94da0d57aa8414118a00d5702ec22ec9c49
parent9067c1f81447fe27bfa7e292e2dd21b29f5959cd (diff)
downloadnihav-606c448ef167017c3171b49f9eb2af82c15079ce.tar.gz
RAD formats crate and Smacker demuxer and decoder
-rw-r--r--nihav-allstuff/Cargo.toml1
-rw-r--r--nihav-allstuff/src/lib.rs6
-rw-r--r--nihav-core/src/detect.rs5
-rw-r--r--nihav-rad/Cargo.toml28
-rw-r--r--nihav-rad/src/codecs/bink2.rs6
-rw-r--r--nihav-rad/src/codecs/binkaud.rs9
-rw-r--r--nihav-rad/src/codecs/binkvid.rs6
-rw-r--r--nihav-rad/src/codecs/mod.rs35
-rw-r--r--nihav-rad/src/codecs/smacker.rs631
-rw-r--r--nihav-rad/src/demuxers/bink.rs12
-rw-r--r--nihav-rad/src/demuxers/mod.rs24
-rw-r--r--nihav-rad/src/demuxers/smacker.rs319
-rw-r--r--nihav-rad/src/lib.rs6
13 files changed, 1088 insertions, 0 deletions
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 <kostya.shishkov@gmail.com>"]
+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<NADecoder> {
+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<NADecoder> {
+unimplemented!("");
+}
+
+pub fn get_decoder_rdft() -> Box<NADecoder> {
+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<NADecoder> {
+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<Codebook<u8>>,
+ 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<u8> 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<u32>,
+ 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<u32> {
+ 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<u8>;
+ fn read_bigtree(&mut self, tree: &mut SmackerTree16) -> DecoderResult<u16>;
+}
+
+impl<'a> ReadTree for BitReader<'a> {
+ fn read_tree8(&mut self, tree: &SmackerTree8) -> DecoderResult<u8> {
+ 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<u16> {
+ 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<NACodecInfo>,
+ 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<u8>,
+ 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<bool> {
+ 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<u8>) {
+ 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<NACodecInfo>) -> 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<NAFrameRef> {
+ 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<NADecoder> {
+ 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<NACodecInfo>) -> 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<NAFrameRef> {
+ 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<NADecoder> {
+ 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<DemuxCore<'a> + '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<u32>,
+ frame_flags: Vec<u8>,
+ video_id: usize,
+ start: u64,
+ cur_frame: usize,
+ queued_packets: Vec<NAPacket>,
+ 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<u8> = 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<NAPacket> {
+ 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<u8> = 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<DemuxCore<'a> + '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