]> git.nihav.org Git - nihav.git/commitdiff
Bink demuxer
authorKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 24 Jan 2019 14:36:04 +0000 (15:36 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 24 Jan 2019 14:36:04 +0000 (15:36 +0100)
nihav-core/src/detect.rs
nihav-rad/src/demuxers/bink.rs

index b2d4295ff9ac1a24938ea5b34ce3c2e28ce53d52..8308ba87ef6f5407a5cc657db3874be64dc0da63 100644 (file)
@@ -193,6 +193,14 @@ const DETECTORS: &[DetectConditions] = &[
         extensions: ".ivr",
         conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b".R1M"), &CC::Str(b".REC"))}],
     },
+    DetectConditions {
+        demux_name: "bink",
+        extensions: ".bik,.kb2",
+        conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::In(Arg::U32BE(0x32494B62),     // BIKb
+                                                               Arg::U32BE(0x32494B7B)),    // BIKz
+                                                       &CC::In(Arg::U32BE(0x4B423261),     // KB2a
+                                                               Arg::U32BE(0x4B42327B)))}], // KB2z
+    },
     DetectConditions {
         demux_name: "smacker",
         extensions: ".smk",
index 879efca57ec21803f671d3d5dd35a147b00facdd..e54ea8c7746bbbb81658dcdd3a238129c3afa6f2 100644 (file)
 use nihav_core::demuxers::*;
 use nihav_core::io::byteio::ByteReader;
 
+struct AudioTrack {
+    id:             usize,
+}
+
+const BINK_AUD_FLAG_DCT:    u8 = 0x10;
+const BINK_AUD_FLAG_STEREO: u8 = 0x20;
+
+impl AudioTrack {
+    fn new(strmgr: &mut StreamManager, srate: u32, flags: u8, str_id: usize) -> DemuxerResult<Self> {
+        let channels = if (flags & BINK_AUD_FLAG_STEREO) != 0 { 2 } else { 1 };
+        let codecname = if (flags & BINK_AUD_FLAG_DCT) != 0 {
+                "bink-audio-dct"
+            } else {
+                "bink-audio-rdft"
+            };
+        let ahdr = NAAudioInfo::new(srate, channels, SND_F32P_FORMAT, 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, srate));
+        validate!(res.is_some());
+        let id = res.unwrap();
+        Ok(Self{ id })
+    }
+}
+
+struct BinkDemuxer<'a> {
+    src:            &'a mut ByteReader<'a>,
+    cur_frame:      usize,
+    frames:         usize,
+    video_id:       usize,
+    tb_num:         u32,
+    tb_den:         u32,
+    ainfo:          Vec<AudioTrack>,
+    queued_packets: Vec<NAPacket>,
+    frame_pos:      Vec<u32>,
+}
+
+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)
+    });
+}
+
+impl<'a> DemuxCore<'a> for BinkDemuxer<'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)?;
+        let magic_tag = mktag!(magic);
+        validate!((magic_tag >= mktag!(b"BIKb") && magic_tag <= mktag!(b"BIKk")) ||
+                  (magic_tag >= mktag!(b"KB2a") && magic_tag <= mktag!(b"KB2k")));
+        let fsize                           = (src.read_u32le()? as usize) + 8;
+        self.frames                         = src.read_u32le()? as usize;
+        let max_size                        = src.read_u32le()? as usize;
+        let _frames2                        = src.read_u32le()? as usize;
+        let width                           = src.read_u32le()? as usize;
+        let height                          = src.read_u32le()? as usize;
+        let tb_den                          = src.read_u32le()?;
+        let tb_num                          = src.read_u32le()?;
+        validate!((width > 0) && (height > 0) && (width <= 7680) && (height <= 4800));
+        validate!((self.frames > 0) && (tb_num > 0) && (tb_den > 0) && (max_size < fsize));
+        let mut flags: [u8; 4] = [0; 4];
+                                              src.read_buf(&mut flags)?;
+        let mut edata: Vec<u8> = Vec::with_capacity(8);
+        edata.resize(8, 0);
+        let p0 = &mut edata[0..4];
+        p0.copy_from_slice(&magic);
+        let p1 = &mut edata[4..][..4];
+        p1.copy_from_slice(&flags);
+        let vhdr = NAVideoInfo::new(width, height, false, YUV420_FORMAT);
+        let codec = if magic[0] == b'K' && magic[1] == b'B' && magic[2] == b'2' { "bink2-video" } else { "bink-video" };
+        let vinfo = NACodecInfo::new(codec, NACodecTypeInfo::Video(vhdr), Some(edata));
+        let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, self.tb_num, self.tb_den));
+        validate!(res.is_some());
+        self.video_id = res.unwrap();
+
+        let num_audio                       = src.read_u32le()? as usize;
+        validate!(num_audio < 256);
+                                              src.read_skip(num_audio * 4)?; // audio max output frame size?
+        self.ainfo = Vec::with_capacity(num_audio);
+        for i in 0..num_audio {
+            let srate                       = src.read_u24le()?;
+            let flags                       = src.read_byte()?;
+            validate!(srate > 0);
+            self.ainfo.push(AudioTrack::new(strmgr, srate, flags, i)?);
+        }
+        for _ in 0..num_audio {
+            let _trk_id                     = src.read_u32le()?;
+        }
+
+        self.frame_pos = Vec::with_capacity(self.frames + 1);
+        for _ in 0..self.frames+1 {
+            let pos                         = src.read_u32le()?;
+            self.frame_pos.push(pos);
+        }
+        validate!((src.tell() as u32) == (self.frame_pos[0] & !1));
+
+        self.cur_frame = 0;
+        
+        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_pos[self.cur_frame + 1] & !1) - (self.frame_pos[self.cur_frame] & !1)) as usize;
+        validate!(payload_size > self.ainfo.len() * 4);
+        for atrk in self.ainfo.iter() {
+            let size                            = self.src.read_u32le()? as usize;
+            validate!(payload_size > size + 4);
+            payload_size -= size + 4;
+
+            let strres = strmgr.get_stream(atrk.id);
+            validate!(strres.is_some());
+            let stream = strres.unwrap();
+            let ts = NATimeInfo::new(Some(self.cur_frame as u64), None, None, self.tb_num, self.tb_den);
+            let pkt = self.src.read_packet(stream.clone(), ts, true, size)?;
+            self.queued_packets.push(pkt);
+        }
+        self.queued_packets.reverse();
+
+        let strres = strmgr.get_stream(self.video_id);
+        validate!(strres.is_some());
+        let stream = strres.unwrap();
+        let keyframe = (self.frame_pos[self.cur_frame] & 1) != 0;
+        let ts = NATimeInfo::new(Some(self.cur_frame as u64), None, None, self.tb_num, self.tb_den);
+        let pkt = self.src.read_packet(stream.clone(), ts, keyframe, payload_size)?;
+
+        self.cur_frame += 1;
+
+        Ok(pkt)
+    }
+    #[allow(unused_variables)]
+    fn seek(&mut self, time: u64) -> DemuxerResult<()> {
+        Err(DemuxerError::NotImplemented)
+    }
+}
+
+impl<'a> BinkDemuxer<'a> {
+    fn new(io: &'a mut ByteReader<'a>) -> Self {
+        Self {
+            src:            io,
+            cur_frame:      0,
+            frames:         0,
+            video_id:       0,
+            tb_num:         0,
+            tb_den:         0,
+            ainfo:          Vec::new(),
+            queued_packets: Vec::new(),
+            frame_pos:      Vec::new(),
+        }
+    }
+}
+
+
 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))
+        Box::new(BinkDemuxer::new(br))
     }
     fn get_name(&self) -> &'static str { "bink" }
 }
 
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::fs::File;
+
+    #[test]
+    fn test_bink_demux() {
+        let mut file = File::open("assets/RAD/ActivisionLogo.bik").unwrap();
+//        let mut file = File::open("assets/RAD/original.bik").unwrap();
+//        let mut file = File::open("assets/RAD/Snd0a110c51.dee").unwrap();
+//        let mut file = File::open("assets/RAD/NEW.BIK").unwrap();
+//        let mut file = File::open("assets/RAD/ge_video_86l.bk2").unwrap();
+        let mut fr = FileReader::new_read(&mut file);
+        let mut br = ByteReader::new(&mut fr);
+        let mut dmx = BinkDemuxer::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);
+        }
+    }
+}