RAD formats crate and Smacker demuxer and decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 23 Jan 2019 13:04:05 +0000 (14:04 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 23 Jan 2019 13:04:05 +0000 (14:04 +0100)
13 files changed:
nihav-allstuff/Cargo.toml
nihav-allstuff/src/lib.rs
nihav-core/src/detect.rs
nihav-rad/Cargo.toml [new file with mode: 0644]
nihav-rad/src/codecs/bink2.rs [new file with mode: 0644]
nihav-rad/src/codecs/binkaud.rs [new file with mode: 0644]
nihav-rad/src/codecs/binkvid.rs [new file with mode: 0644]
nihav-rad/src/codecs/mod.rs [new file with mode: 0644]
nihav-rad/src/codecs/smacker.rs [new file with mode: 0644]
nihav-rad/src/demuxers/bink.rs [new file with mode: 0644]
nihav-rad/src/demuxers/mod.rs [new file with mode: 0644]
nihav-rad/src/demuxers/smacker.rs [new file with mode: 0644]
nihav-rad/src/lib.rs [new file with mode: 0644]

index 5003f752ddec2f6608c326ab192905d275f316cf..152739c2a39332c30a4520350fed3760612d2b2c 100644 (file)
@@ -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
index 667bcd0946730c5f70d8abb940098da646b1cdd6..88c5f451335da8d8c24d05904c948998e6ca7fd4 100644 (file)
@@ -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);
 }
index 0b606419b0c91f86b495482e56d0332e39d4de25..b2b34148c27d2e1164abe7e1ac69d0b141c7e995 100644 (file)
@@ -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 (file)
index 0000000..e32dcd8
--- /dev/null
@@ -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 (file)
index 0000000..2777e5f
--- /dev/null
@@ -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 (file)
index 0000000..ba8f707
--- /dev/null
@@ -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 (file)
index 0000000..2777e5f
--- /dev/null
@@ -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 (file)
index 0000000..4ce5563
--- /dev/null
@@ -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 (file)
index 0000000..e6f4268
--- /dev/null
@@ -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 (file)
index 0000000..879efca
--- /dev/null
@@ -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 (file)
index 0000000..a1234bd
--- /dev/null
@@ -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 (file)
index 0000000..b666cf1
--- /dev/null
@@ -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 (file)
index 0000000..f723e9e
--- /dev/null
@@ -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