use std::path::Path;
use anyhow::{Context, Result};
use container::streaming;
#[derive(Debug, Clone)]
pub struct MediaInfo {
pub container: String,
pub video_codec: String,
pub width: u32,
pub height: u32,
pub frame_rate: f64,
pub duration: f64,
pub pixel_format: String,
pub audio: Option<AudioStreamInfo>,
}
#[derive(Debug, Clone)]
pub struct AudioStreamInfo {
pub codec: String,
pub sample_rate: u32,
pub channels: u16,
}
pub fn probe_file(input: impl AsRef<Path>) -> Result<MediaInfo> {
let input = input.as_ref();
let bytes = std::fs::read(input)
.with_context(|| format!("reading input file {}", input.display()))?;
probe_bytes(&bytes)
}
pub fn probe_bytes(input: &[u8]) -> Result<MediaInfo> {
let demuxer = streaming::demux_streaming(input).context("demux")?;
let header = demuxer.header();
let audio = demuxer.audio().map(|t| AudioStreamInfo {
codec: t.codec.to_ascii_lowercase(),
sample_rate: t.sample_rate,
channels: t.channels,
});
Ok(MediaInfo {
container: detect_container(input).to_string(),
video_codec: header.codec.to_ascii_lowercase(),
width: header.info.width,
height: header.info.height,
frame_rate: header.info.frame_rate,
duration: header.info.duration,
pixel_format: format!("{:?}", header.info.pixel_format),
audio,
})
}
fn detect_container(data: &[u8]) -> &'static str {
if data.len() < 12 {
return "unknown";
}
if &data[4..8] == b"ftyp" || &data[4..8] == b"moov" || &data[4..8] == b"mdat" {
return "mp4";
}
if data[0] == 0x1A && data[1] == 0x45 && data[2] == 0xDF && data[3] == 0xA3 {
return "mkv";
}
if &data[..4] == b"RIFF" && &data[8..12] == b"AVI " {
return "avi";
}
if data[0] == 0x47
&& data.len() > 188
&& data[188] == 0x47
&& (data.len() <= 376 || data[376] == 0x47)
{
return "ts";
}
"unknown"
}