use crate::format::{MediaType, PixelFormat, SampleFormat};
use crate::rational::Rational;
use crate::time::TimeBase;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CodecId(pub String);
impl CodecId {
pub fn new(s: impl Into<String>) -> Self {
Self(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for CodecId {
fn from(s: &str) -> Self {
Self(s.to_owned())
}
}
impl std::fmt::Display for CodecId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum CodecTag {
Fourcc([u8; 4]),
WaveFormat(u16),
Mp4ObjectType(u8),
Matroska(String),
}
impl CodecTag {
pub fn fourcc(raw: &[u8; 4]) -> Self {
let mut out = [0u8; 4];
for i in 0..4 {
out[i] = raw[i].to_ascii_uppercase();
}
Self::Fourcc(out)
}
pub fn wave_format(tag: u16) -> Self {
Self::WaveFormat(tag)
}
pub fn mp4_object_type(oti: u8) -> Self {
Self::Mp4ObjectType(oti)
}
pub fn matroska(id: impl Into<String>) -> Self {
Self::Matroska(id.into())
}
}
impl std::fmt::Display for CodecTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fourcc(fcc) => {
if fcc.iter().all(|b| b.is_ascii_graphic() || *b == b' ') {
write!(f, "fourcc({})", std::str::from_utf8(fcc).unwrap_or("????"))
} else {
write!(
f,
"fourcc(0x{:02X}{:02X}{:02X}{:02X})",
fcc[0], fcc[1], fcc[2], fcc[3]
)
}
}
Self::WaveFormat(t) => write!(f, "wFormatTag(0x{t:04X})"),
Self::Mp4ObjectType(o) => write!(f, "mp4_oti(0x{o:02X})"),
Self::Matroska(s) => write!(f, "matroska({s})"),
}
}
}
#[derive(Clone, Debug)]
pub struct CodecParameters {
pub codec_id: CodecId,
pub media_type: MediaType,
pub sample_rate: Option<u32>,
pub channels: Option<u16>,
pub sample_format: Option<SampleFormat>,
pub width: Option<u32>,
pub height: Option<u32>,
pub pixel_format: Option<PixelFormat>,
pub frame_rate: Option<Rational>,
pub extradata: Vec<u8>,
pub bit_rate: Option<u64>,
}
impl CodecParameters {
pub fn audio(codec_id: CodecId) -> Self {
Self {
codec_id,
media_type: MediaType::Audio,
sample_rate: None,
channels: None,
sample_format: None,
width: None,
height: None,
pixel_format: None,
frame_rate: None,
extradata: Vec::new(),
bit_rate: None,
}
}
pub fn matches_core(&self, other: &CodecParameters) -> bool {
self.codec_id == other.codec_id
&& self.sample_rate == other.sample_rate
&& self.channels == other.channels
&& self.sample_format == other.sample_format
&& self.width == other.width
&& self.height == other.height
&& self.pixel_format == other.pixel_format
}
pub fn video(codec_id: CodecId) -> Self {
Self {
codec_id,
media_type: MediaType::Video,
sample_rate: None,
channels: None,
sample_format: None,
width: None,
height: None,
pixel_format: None,
frame_rate: None,
extradata: Vec::new(),
bit_rate: None,
}
}
}
#[derive(Clone, Debug)]
pub struct StreamInfo {
pub index: u32,
pub time_base: TimeBase,
pub duration: Option<i64>,
pub start_time: Option<i64>,
pub params: CodecParameters,
}
#[cfg(test)]
mod codec_tag_tests {
use super::*;
#[test]
fn fourcc_uppercases_on_construction() {
let t = CodecTag::fourcc(b"div3");
assert_eq!(t, CodecTag::Fourcc(*b"DIV3"));
let t2 = CodecTag::fourcc(b"MP42");
assert_eq!(t2, CodecTag::Fourcc(*b"MP42"));
let t3 = CodecTag::fourcc(&[0xFF, b'a', 0x00, b'1']);
assert_eq!(t3, CodecTag::Fourcc([0xFF, b'A', 0x00, b'1']));
}
#[test]
fn fourcc_equality_case_insensitive_via_ctor() {
assert_eq!(CodecTag::fourcc(b"xvid"), CodecTag::fourcc(b"XVID"));
assert_eq!(CodecTag::fourcc(b"DiV3"), CodecTag::fourcc(b"div3"));
}
#[test]
fn display_printable_fourcc() {
assert_eq!(CodecTag::fourcc(b"XVID").to_string(), "fourcc(XVID)");
}
#[test]
fn display_non_printable_fourcc_as_hex() {
let t = CodecTag::Fourcc([0x00, 0x00, 0x00, 0x01]);
assert_eq!(t.to_string(), "fourcc(0x00000001)");
}
#[test]
fn display_wave_format() {
assert_eq!(
CodecTag::wave_format(0x0055).to_string(),
"wFormatTag(0x0055)"
);
}
#[test]
fn display_mp4_oti() {
assert_eq!(CodecTag::mp4_object_type(0x40).to_string(), "mp4_oti(0x40)");
}
#[test]
fn display_matroska() {
assert_eq!(
CodecTag::matroska("V_MPEG4/ISO/AVC").to_string(),
"matroska(V_MPEG4/ISO/AVC)",
);
}
}