#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ContainerFormat {
#[default]
Unknown,
Mp4,
Avi,
Mov,
Mkv,
WebM,
Flv,
Wmv,
Mjpeg,
}
impl ContainerFormat {
pub fn extension(&self) -> &'static str {
match self {
ContainerFormat::Unknown => "bin",
ContainerFormat::Mp4 => "mp4",
ContainerFormat::Avi => "avi",
ContainerFormat::Mov => "mov",
ContainerFormat::Mkv => "mkv",
ContainerFormat::WebM => "webm",
ContainerFormat::Flv => "flv",
ContainerFormat::Wmv => "wmv",
ContainerFormat::Mjpeg => "mjpeg",
}
}
pub fn mime_type(&self) -> &'static str {
match self {
ContainerFormat::Unknown => "application/octet-stream",
ContainerFormat::Mp4 => "video/mp4",
ContainerFormat::Avi => "video/x-msvideo",
ContainerFormat::Mov => "video/quicktime",
ContainerFormat::Mkv => "video/x-matroska",
ContainerFormat::WebM => "video/webm",
ContainerFormat::Flv => "video/x-flv",
ContainerFormat::Wmv => "video/x-ms-wmv",
ContainerFormat::Mjpeg => "video/x-motion-jpeg",
}
}
}
pub fn detect_container_format(data: &[u8]) -> ContainerFormat {
if data.is_empty() {
return ContainerFormat::Unknown;
}
if data.len() >= 2 && data[0] == 0xFF && data[1] == 0xD8 {
return ContainerFormat::Mjpeg;
}
if data.len() >= 3 && data[0] == b'F' && data[1] == b'L' && data[2] == b'V' {
return ContainerFormat::Flv;
}
if data.len() < 8 {
return ContainerFormat::Unknown;
}
if data.len() >= 8 && &data[4..8] == b"ftyp" {
return ContainerFormat::Mp4;
}
if data.len() >= 12 && &data[0..4] == b"RIFF" && &data[8..12] == b"AVI " {
return ContainerFormat::Avi;
}
if data.len() >= 8 && &data[4..8] == b"moov" {
return ContainerFormat::Mov;
}
if data.len() >= 4 && data[0] == 0x1A && data[1] == 0x45 && data[2] == 0xDF && data[3] == 0xA3 {
if data.len() > 48 && data.windows(4).skip(8).any(|w| w == b"webm") {
return ContainerFormat::WebM;
}
return ContainerFormat::Mkv;
}
if data.len() >= 16 && &data[0..16] == b"0&\xb2u\x8ef\xcf\x11\xa6\xd9\x00\xaa\x00b\xcel" {
return ContainerFormat::Wmv;
}
ContainerFormat::Unknown
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_mp4() {
let data = b"\x00\x00\x00\x20ftypmp42\x00\x00\x00\x00";
assert_eq!(detect_container_format(data), ContainerFormat::Mp4);
}
#[test]
fn test_detect_avi() {
let mut data = b"RIFF".to_vec();
data.extend_from_slice(&[0u8; 4]);
data.extend_from_slice(b"AVI ");
assert_eq!(detect_container_format(&data), ContainerFormat::Avi);
}
#[test]
fn test_detect_flv() {
assert_eq!(detect_container_format(b"FLV\x01"), ContainerFormat::Flv);
}
#[test]
fn test_detect_mkv() {
let data = b"\x1A\x45\xDF\xA3\x01\x00\x00\x00";
assert_eq!(detect_container_format(data), ContainerFormat::Mkv);
}
#[test]
fn test_detect_unknown() {
assert_eq!(detect_container_format(b"not video"), ContainerFormat::Unknown);
}
#[test]
fn test_detect_mjpeg() {
assert_eq!(detect_container_format(&[0xFF, 0xD8, 0xFF, 0xE0]), ContainerFormat::Mjpeg);
}
#[test]
fn test_format_extensions() {
assert_eq!(ContainerFormat::Mp4.extension(), "mp4");
assert_eq!(ContainerFormat::Mkv.extension(), "mkv");
assert_eq!(ContainerFormat::WebM.extension(), "webm");
assert_eq!(ContainerFormat::Flv.extension(), "flv");
assert_eq!(ContainerFormat::Mjpeg.extension(), "mjpeg");
}
}