diff options
author | Kostya Shishkov <kostya.shishkov@gmail.com> | 2020-02-20 11:35:16 +0100 |
---|---|---|
committer | Kostya Shishkov <kostya.shishkov@gmail.com> | 2020-02-20 11:35:16 +0100 |
commit | 32f7cbe538d71574f7ac05aa51599d2678f5db3f (patch) | |
tree | 27ada919c11200139c5fee4f2f299dde373476fa /nihav-core/src | |
parent | b4d5b8515e75383b4fc59ea2813c90c615d59a96 (diff) | |
download | nihav-32f7cbe538d71574f7ac05aa51599d2678f5db3f.tar.gz |
split nihav-registry from nihav-core
Diffstat (limited to 'nihav-core/src')
-rw-r--r-- | nihav-core/src/detect.rs | 328 | ||||
-rw-r--r-- | nihav-core/src/lib.rs | 3 | ||||
-rw-r--r-- | nihav-core/src/register.rs | 284 |
3 files changed, 0 insertions, 615 deletions
diff --git a/nihav-core/src/detect.rs b/nihav-core/src/detect.rs deleted file mode 100644 index a693124..0000000 --- a/nihav-core/src/detect.rs +++ /dev/null @@ -1,328 +0,0 @@ -//! Container format detection. -//! -//! Usually user does not know the container format of the opened file. -//! That is why format detection functionality is needed. -//! This module contains the set of rules to detect container not merely by file extension but also by its content if possible. -//! -//! # Examples -//! -//! ```no_run -//! use nihav_core::detect::detect_format; -//! use std::fs::File; -//! use nihav_core::io::byteio::*; -//! -//! let name = "mediafile.ogv"; -//! let mut file = File::open(name).unwrap(); -//! let mut filereader = FileReader::new_read(&mut file); -//! let mut br = ByteReader::new(&mut filereader); -//! let result = detect_format(name, &mut br); -//! if let Some((name, score)) = result { -//! println!("detected format {} with score {:?}", name, score); -//! } -//! ``` -use std::io::SeekFrom; -use crate::io::byteio::ByteReader; - -/// Format detection score. -#[derive(Debug,Clone,Copy,PartialEq)] -pub enum DetectionScore { - /// Format is not detected. - No, - /// Format matched by file extension. - ExtensionMatches, - /// Format matches by markers inside the file. - MagicMatches, -} - -impl DetectionScore { - /// Checks whether current detection score is less than a value it is compared against. - pub fn less(self, other: DetectionScore) -> bool { - (self as i32) < (other as i32) - } -} - -#[allow(dead_code)] -enum Arg { - Byte(u8), - U16BE(u16), - U16LE(u16), - U24BE(u32), - U24LE(u32), - U32BE(u32), - U32LE(u32), - U64BE(u64), - U64LE(u64), -} - -impl Arg { - fn val(&self) -> u64 { - match *self { - Arg::Byte(b) => { u64::from(b) } - Arg::U16BE(v) => { u64::from(v) } - Arg::U16LE(v) => { u64::from(v) } - Arg::U24BE(v) => { u64::from(v) } - Arg::U24LE(v) => { u64::from(v) } - Arg::U32BE(v) => { u64::from(v) } - Arg::U32LE(v) => { u64::from(v) } - Arg::U64BE(v) => { v } - Arg::U64LE(v) => { v } - } - } - fn read_val(&self, src: &mut ByteReader) -> Option<u64> { - match *self { - Arg::Byte(_) => { - let res = src.peek_byte(); - if res.is_err() { return None; } - Some(u64::from(res.unwrap())) - } - Arg::U16BE(_) => { - let res = src.peek_u16be(); - if res.is_err() { return None; } - Some(u64::from(res.unwrap())) - } - Arg::U16LE(_) => { - let res = src.peek_u16le(); - if res.is_err() { return None; } - Some(u64::from(res.unwrap())) - } - Arg::U24BE(_) => { - let res = src.peek_u24be(); - if res.is_err() { return None; } - Some(u64::from(res.unwrap())) - } - Arg::U24LE(_) => { - let res = src.peek_u24le(); - if res.is_err() { return None; } - Some(u64::from(res.unwrap())) - } - Arg::U32BE(_) => { - let res = src.peek_u32be(); - if res.is_err() { return None; } - Some(u64::from(res.unwrap())) - } - Arg::U32LE(_) => { - let res = src.peek_u32le(); - if res.is_err() { return None; } - Some(u64::from(res.unwrap())) - } - Arg::U64BE(_) => { - let res = src.peek_u64be(); - if res.is_err() { return None; } - Some(res.unwrap()) - } - Arg::U64LE(_) => { - let res = src.peek_u64le(); - if res.is_err() { return None; } - Some(res.unwrap()) - } - } - } - fn eq(&self, src: &mut ByteReader) -> bool { - let val = self.read_val(src); - if val.is_none() { false } - else { val.unwrap() == self.val() } - } - fn ge(&self, src: &mut ByteReader) -> bool { - let val = self.read_val(src); - if val.is_none() { false } - else { val.unwrap() >= self.val() } - } - fn gt(&self, src: &mut ByteReader) -> bool { - let val = self.read_val(src); - if val.is_none() { false } - else { val.unwrap() > self.val() } - } - fn le(&self, src: &mut ByteReader) -> bool { - let val = self.read_val(src); - if val.is_none() { false } - else { val.unwrap() <= self.val() } - } - fn lt(&self, src: &mut ByteReader) -> bool { - let val = self.read_val(src); - if val.is_none() { false } - else { val.unwrap() < self.val() } - } -} - -#[allow(dead_code)] -enum CC<'a> { - Or(&'a CC<'a>, &'a CC<'a>), - Eq(Arg), - Str(&'static [u8]), - In(Arg, Arg), - Lt(Arg), - Le(Arg), - Gt(Arg), - Ge(Arg), -} - -impl<'a> CC<'a> { - fn eval(&self, src: &mut ByteReader) -> bool { - match *self { - CC::Or (ref a, ref b) => { a.eval(src) || b.eval(src) }, - CC::Eq(ref arg) => { arg.eq(src) }, - CC::In(ref a, ref b) => { a.ge(src) && b.le(src) }, - CC::Lt(ref arg) => { arg.lt(src) }, - CC::Le(ref arg) => { arg.le(src) }, - CC::Gt(ref arg) => { arg.gt(src) }, - CC::Ge(ref arg) => { arg.ge(src) }, - CC::Str(str) => { - let mut val: Vec<u8> = vec![0; str.len()]; - let res = src.peek_buf(val.as_mut_slice()); - if res.is_err() { return false; } - val == str - } - } - } -} - -struct CheckItem<'a> { - offs: u32, - cond: &'a CC<'a>, -} - -#[allow(dead_code)] -struct DetectConditions<'a> { - demux_name: &'static str, - extensions: &'static str, - conditions: &'a [CheckItem<'a>], -} - -const DETECTORS: &[DetectConditions] = &[ - DetectConditions { - demux_name: "avi", - extensions: ".avi", - conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b"RIFF"), &CC::Str(b"ON2 ")) }, - CheckItem{offs: 8, cond: &CC::Or(&CC::Or(&CC::Str(b"AVI LIST"), - &CC::Str(b"AVIXLIST")), - &CC::Str(b"ON2fLIST")) }, - ] - }, - DetectConditions { - demux_name: "gdv", - extensions: ".gdv", - conditions: &[CheckItem{offs: 0, cond: &CC::Eq(Arg::U32LE(0x29111994))}], - }, - DetectConditions { - demux_name: "realaudio", - extensions: ".ra,.ram", - conditions: &[CheckItem{offs: 0, cond: &CC::Str(b".ra\xFD")}], - }, - DetectConditions { - demux_name: "realmedia", - extensions: ".rm,.rmvb,.rma,.ra,.ram", - conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b".RMF"), &CC::Str(b".RMP")) }, - CheckItem{offs: 4, cond: &CC::Ge(Arg::U32BE(10))}], - }, - DetectConditions { - demux_name: "real_ivr", - extensions: ".ivr", - conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b".R1M"), &CC::Str(b".REC"))}], - }, - DetectConditions { - demux_name: "bink", - extensions: ".bik,.bk2", - 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", - conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b"SMK2"), &CC::Str(b"SMK4"))}], - }, - DetectConditions { - demux_name: "bmv", - extensions: ".bmv", - conditions: &[], - }, - DetectConditions { - demux_name: "bmv3", - extensions: ".bmv", - conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"BMVi") }, - CheckItem{offs: 32, cond: &CC::Str(b"DATA")}], - }, - DetectConditions { - demux_name: "vmd", - extensions: ".vmd", - conditions: &[], - }, -]; - -/// Tries to detect container format. -/// -/// This function tries to determine container format using both file extension and checking against container specific markers inside. -/// In case of success the function returns short container name and the detection score. -/// Result should have the highest detection score among tested. -pub fn detect_format(name: &str, src: &mut ByteReader) -> Option<(&'static str, DetectionScore)> { - let mut result = None; - let lname = name.to_lowercase(); - for detector in DETECTORS { - let mut score = DetectionScore::No; - if !name.is_empty() { - for ext in detector.extensions.split(',') { - if lname.ends_with(ext) { - score = DetectionScore::ExtensionMatches; - break; - } - } - } - let mut passed = !detector.conditions.is_empty(); - for ck in detector.conditions { - let ret = src.seek(SeekFrom::Start(u64::from(ck.offs))); - if ret.is_err() { - passed = false; - break; - } - if !ck.cond.eval(src) { - passed = false; - break; - } - } - if passed { - score = DetectionScore::MagicMatches; - } - if score == DetectionScore::MagicMatches { - return Some((detector.demux_name, score)); - } - if result.is_none() && score != DetectionScore::No { - result = Some((detector.demux_name, score)); - } else if result.is_some() { - let (_, oldsc) = result.unwrap(); - if oldsc.less(score) { - result = Some((detector.demux_name, score)); - } - } - } - result -} - -#[cfg(test)] -mod test { - use super::*; - use std::fs::File; - use crate::io::byteio::*; - - #[test] - fn test_avi_detect() { - let name = "assets/Indeo/laser05.avi"; - let mut file = File::open(name).unwrap(); - let mut fr = FileReader::new_read(&mut file); - let mut br = ByteReader::new(&mut fr); - let (name, score) = detect_format(name, &mut br).unwrap(); - assert_eq!(name, "avi"); - assert_eq!(score, DetectionScore::MagicMatches); - } - - #[test] - fn test_gdv_detect() { - let name = "assets/Game/intro1.gdv"; - let mut file = File::open(name).unwrap(); - let mut fr = FileReader::new_read(&mut file); - let mut br = ByteReader::new(&mut fr); - let (name, score) = detect_format(name, &mut br).unwrap(); - assert_eq!(name, "gdv"); - assert_eq!(score, DetectionScore::MagicMatches); - } -} diff --git a/nihav-core/src/lib.rs b/nihav-core/src/lib.rs index a53f304..cf97841 100644 --- a/nihav-core/src/lib.rs +++ b/nihav-core/src/lib.rs @@ -15,9 +15,6 @@ pub mod frame; #[allow(clippy::too_many_arguments)] pub mod io; pub mod refs; -pub mod register; -#[allow(clippy::unreadable_literal)] -pub mod detect; pub mod reorder; pub mod scale; pub mod soundcvt; diff --git a/nihav-core/src/register.rs b/nihav-core/src/register.rs deleted file mode 100644 index c54ce58..0000000 --- a/nihav-core/src/register.rs +++ /dev/null @@ -1,284 +0,0 @@ -//! Global registry of codec information. -//! -//! This module contains codec information from technical level that allows user to retrieve information about codec type and features without creating and invoking a decoder for such codec. -use std::fmt; - -/// Codec types. -#[derive(Debug,Clone,Copy,PartialEq)] -#[allow(dead_code)] -pub enum CodecType { - /// Video codec. - Video, - /// Audio codec. - Audio, - /// Subtitle codec. - Subtitles, - /// Some special codec (e.g. some event stream or separate timecodes stream). - Data, - /// Dummy codec. - None, -} - -impl fmt::Display for CodecType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - CodecType::Video => write!(f, "Video"), - CodecType::Audio => write!(f, "Audio"), - CodecType::Subtitles => write!(f, "Subtitles"), - CodecType::Data => write!(f, "Data"), - CodecType::None => write!(f, "-"), - } - } -} - -const CODEC_CAP_INTRAONLY:u32 = 0x0001; -const CODEC_CAP_LOSSLESS:u32 = 0x0002; -const CODEC_CAP_REORDER:u32 = 0x0004; -const CODEC_CAP_HYBRID:u32 = 0x0008; -const CODEC_CAP_SCALABLE:u32 = 0x0010; - -/// Codec description structure. -#[derive(Clone)] -pub struct CodecDescription { - /// Short codec name. - /// - /// Short codec name is used inside NihAV as the unique identifier. - pub name: &'static str, - /// Full codec name. - pub fname: &'static str, - /// Codec type. - pub ctype: CodecType, - /// Codec capabilities. - pub caps: u32, -} - -impl CodecDescription { - /// Returns short codec name. - pub fn get_name(&self) -> &'static str { self.name } - /// Returns full codec name. - pub fn get_full_name(&self) -> &'static str { self.fname } - /// Returns codec type. - pub fn get_codec_type(&self) -> CodecType { self.ctype } - /// Reports whether the codec has only intra frames or not. - pub fn is_intraonly(&self) -> bool { (self.caps & CODEC_CAP_INTRAONLY) != 0 } - /// Reports whether the codec is lossless. - pub fn is_lossless(&self) -> bool { (self.caps & CODEC_CAP_LOSSLESS) != 0 } - /// Reports whether the codec requires frame reordering. - pub fn has_reorder(&self) -> bool { (self.caps & CODEC_CAP_REORDER) != 0 } - /// Reports whether the codec can be either lossless or lossy. - pub fn is_hybrid(&self) -> bool { (self.caps & CODEC_CAP_HYBRID) != 0 } - /// Reports whether codec supports scalability. - /// - /// Scalability means that codec can be decoded in reduced resolution by design. - pub fn is_scalable(&self) -> bool { (self.caps & CODEC_CAP_SCALABLE) != 0 } -} - -impl fmt::Display for CodecDescription { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut out = self.fname.to_string(); - if self.caps != 0 { - let mut capfmt = "".to_string(); - if (self.caps & CODEC_CAP_INTRAONLY) != 0 { - capfmt = format!("{} Intra-only", capfmt); - } - if (self.caps & CODEC_CAP_LOSSLESS) != 0 { - capfmt = format!("{} Lossless", capfmt); - } - if (self.caps & CODEC_CAP_REORDER) != 0 { - capfmt = format!("{} Frame reorder", capfmt); - } - if (self.caps & CODEC_CAP_HYBRID) != 0 { - capfmt = format!("{} Can be lossy and lossless", capfmt); - } - if (self.caps & CODEC_CAP_SCALABLE) != 0 { - capfmt = format!("{} Scalable", capfmt); - } - out = format!("{} ({})", out, capfmt); - } - write!(f, "{}", out) - } -} - -macro_rules! desc { - (video; $n:expr, $fn:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Video, - caps: 0 } - }); - (video; $n:expr, $fn:expr, $c:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Video, - caps: $c } - }); - (video-ll; $n:expr, $fn:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Video, - caps: CODEC_CAP_LOSSLESS | CODEC_CAP_INTRAONLY } - }); - (video-llp; $n:expr, $fn:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Video, - caps: CODEC_CAP_LOSSLESS } - }); - (video-im; $n:expr, $fn:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Video, - caps: CODEC_CAP_INTRAONLY } - }); - (video-modern; $n:expr, $fn:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Video, - caps: CODEC_CAP_REORDER | CODEC_CAP_HYBRID } - }); - (audio; $n:expr, $fn:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Audio, - caps: 0 } - }); - (audio-ll; $n:expr, $fn:expr) => ({ - CodecDescription{ name: $n, fname: $fn, ctype: CodecType::Audio, - caps: CODEC_CAP_LOSSLESS | CODEC_CAP_INTRAONLY } - }); -} - -/// Returns codec description for the provided codec short name if it is found. -pub fn get_codec_description(name: &str) -> Option<&'static CodecDescription> { - for reg in CODEC_REGISTER { - if reg.name == name { - return Some(reg); - } - } - None -} - -static CODEC_REGISTER: &'static [CodecDescription] = &[ - desc!(audio-ll; "pcm", "PCM"), - - desc!(video-im; "indeo1", "Intel Raw IF09"), - desc!(video-im; "indeo2", "Intel Indeo 2"), - desc!(video; "indeo3", "Intel Indeo 3"), - desc!(video; "indeo4", "Intel Indeo 4", CODEC_CAP_REORDER | CODEC_CAP_SCALABLE), - desc!(video; "indeo5", "Intel Indeo 5", CODEC_CAP_REORDER | CODEC_CAP_SCALABLE), - desc!(video; "intel263", "Intel I263", CODEC_CAP_REORDER), - desc!(audio; "iac", "Intel Indeo audio"), - desc!(audio; "imc", "Intel Music Coder"), - - desc!(video; "realvideo1", "Real Video 1"), - desc!(video; "realvideo2", "Real Video 2"), - desc!(video; "realvideo3", "Real Video 3", CODEC_CAP_REORDER), - desc!(video; "realvideo4", "Real Video 4", CODEC_CAP_REORDER), - desc!(video; "realvideo6", "Real Video 6", CODEC_CAP_REORDER), - desc!(video; "clearvideo", "ClearVideo"), - desc!(video; "clearvideo_rm", "ClearVideo"), - desc!(audio; "ra14.4", "RealAudio 14.4"), - desc!(audio; "ra28.8", "RealAudio 28.8"), - desc!(audio; "cook", "RealAudio Cooker"), - desc!(audio; "ralf", "RealAudio Lossless"), - desc!(audio; "aac", "AAC"), - desc!(audio; "ac3", "ETSI TS 102 366"), - desc!(audio; "atrac3", "Sony Atrac3"), - desc!(audio; "sipro", "Sipro Labs ADPCM"), - - desc!(video; "truemotion1", "TrueMotion 1"), - desc!(video-im; "truemotionrt", "TrueMotion RT"), - desc!(video; "truemotion2", "TrueMotion 2"), - desc!(video; "truemotion2x", "TrueMotion 2X"), - desc!(video; "vp3", "VP3"), - desc!(video; "vp4", "VP4"), - desc!(video; "vp5", "VP5"), - desc!(video; "vp6", "VP6"), - desc!(video; "vp6a", "VP6"), - desc!(video; "vp7", "VP7"), - desc!(video; "vp8", "VP8"), - desc!(video; "vp9", "VP9"), - desc!(audio; "adpcm-dk3", "Duck DK3 ADPCM"), - desc!(audio; "adpcm-dk4", "Duck DK4 ADPCM"), - desc!(audio; "on2avc-500", "On2 AVC"), - desc!(audio; "on2avc-501", "On2 AVC"), - - desc!(video; "gdv-video", "Gremlin Digital Video - video"), - desc!(audio; "gdv-audio", "Gremlin Digital Video - audio"), - desc!(video; "bmv-video", "BMV video"), - desc!(audio; "bmv-audio", "BMV audio"), - desc!(video; "bmv3-video", "DW Noir BMV video"), - desc!(audio; "bmv3-audio", "DW Noir BMV audio"), - desc!(video; "midivid", "MidiVid"), - desc!(video; "midivid3", "MidiVid 3"), - desc!(video-ll; "midivid-ll", "MidiVid Lossless"), - desc!(video; "vmd-video", "VMD video"), - desc!(audio; "vmd-audio", "VMD audio"), - - desc!(video; "smacker-video", "Smacker video"), - desc!(audio; "smacker-audio", "Smacker audio"), - desc!(video; "bink-video", "Bink video"), - desc!(video; "bink2-video", "Bink2 video"), - desc!(audio; "bink-audio-dct", "Bink audio (DCT)"), - desc!(audio; "bink-audio-rdft", "Bink audio (RDFT)"), -]; - -static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[ - (b"IF09", "indeo1"), - (b"RT21", "indeo2"), - (b"IV31", "indeo3"), - (b"IV32", "indeo3"), - (b"IV41", "indeo4"), - (b"IV50", "indeo5"), - (b"I263", "intel263"), - - (b"UCOD", "clearvideo"), - - (b"MVDV", "midivid"), - (b"MV30", "midivid3"), - (b"MVLZ", "midivid-ll"), - - (b"DUCK", "truemotion1"), - (b"TR20", "truemotionrt"), - (b"TM20", "truemotion2"), - (b"TM2A", "truemotion2x"), - (b"TM2X", "truemotion2x"), - (b"VP30", "vp3"), - (b"VP31", "vp3"), - (b"VP40", "vp4"), - (b"VP50", "vp5"), - (b"VP60", "vp6"), - (b"VP61", "vp6"), - (b"VP62", "vp6"), - (b"VP6A", "vp6a"), - (b"VP70", "vp7"), -]; - -static WAV_CODEC_REGISTER: &'static [(u16, &str)] = &[ - (0x0000, "pcm"), - (0x0001, "pcm"), - (0x0003, "pcm"), - (0x0061, "adpcm-dk4"), - (0x0062, "adpcm-dk3"), - (0x0401, "imc"), - (0x0402, "iac"), - (0x0500, "on2avc-500"), - (0x0501, "on2avc-501"), -]; - -/// Returns video codec short name for provided FOURCC (used in AVI format). -pub fn find_codec_from_avi_fourcc(fcc: &[u8;4]) -> Option<&'static str> { - for (fourcc, name) in AVI_VIDEO_CODEC_REGISTER.iter() { - if *fourcc == fcc { return Some(name); } - } - None -} - -/// Returns known audio codec short name for provided TWOCC (used in WAV and AVI format). -pub fn find_codec_from_wav_twocc(tcc: u16) -> Option<&'static str> { - for (twocc, name) in WAV_CODEC_REGISTER.iter() { - if *twocc == tcc { return Some(name); } - } - None -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_register() { - let c1 = find_codec_from_avi_fourcc(b"IV41").unwrap(); - let c2 = find_codec_from_wav_twocc(0x401).unwrap(); - println!("found {} and {}", c1, c2); - let cd1 = get_codec_description(c1).unwrap(); - let cd2 = get_codec_description(c2).unwrap(); - println!("got {} and {}", cd1, cd2); - } -} |