So I’ve decided to implement container format detection for NihAV. This is a work of progress and I’m pretty sure I’ll change it later but it should do for now.
The main principles are quite simple: formats are detected by extension and by the contents, so there’s a score for it:
pub enum DetectionScore { No, ExtensionMatches, MagicMatches, }
I don’t see why some format should not be detected properly if demuxer for it is disabled or not implemented at all. So in NihAV there’s a specific detect
module that offers just one function:
pub fn detect_format(name: &str, src: &mut ByteReader) -> Option< (&'static str, DetectionScore)>;
It takes input filename and source stream reader and then tries to determine whether some format matches and returns format name and detection score on success (or nothing otherwise). I might add probing individual format later if I feel like it.
Before I explain how detection works let me quote the source of the detection array (in hope that it will explain a lot by itself):
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))}], }, ];
So what is the way to detect format? First the name is matched to see whether one of the listed extensions fits, then the file contents are checked for markers inside. These checks are descriptions like “check that at offset X there’s data of type <type>
that (equals/less than/greater than) Y”. Also you can specify several alternative checks for the same offset and there’s range check condition too.
This way I can describe most sane formats, like “if at offset 1024 you have tag M.K.
then it’s ProTracker module” or “if it starts with BM
and 16-bit LE value here is less than this and here it’s in range 1-16 then this must be BMP”.
One might wonder how well it would work on MP3s renamed to “.acm” (IIRC one game did that). I’ll reveal the secret: it won’t work at all. Dealing with raw streams is actually beside format detector because it is raw stream and not a container format. You can create raw stream demuxer, then try all possible chunkers to see which one fit but that is stuff for the upper layer (maybe it will be implemented there inside the input stream handling function eventually). NihAV is not a place for automagic things.