use core::fmt;
use ffmpeg_next::ffi::AVCodecID;
use smol_bytes::Utf8Bytes;
const DESCRIPTOR_TEXT_MAX_BYTES: usize = 1024;
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct CodecId(i32);
impl CodecId {
#[inline]
pub const fn from_raw(raw: i32) -> Self {
Self(raw)
}
#[inline]
pub const fn raw(self) -> i32 {
self.0
}
pub fn name(self) -> Option<Utf8Bytes> {
let descriptor = self.descriptor();
if descriptor.is_null() {
return None;
}
let name = unsafe { core::ptr::addr_of!((*descriptor).name).read() };
unsafe { crate::ffi::table_text(name, DESCRIPTOR_TEXT_MAX_BYTES) }.map(Utf8Bytes::from_static)
}
pub fn long_name(self) -> Option<Utf8Bytes> {
let descriptor = self.descriptor();
if descriptor.is_null() {
return None;
}
let long_name = unsafe { core::ptr::addr_of!((*descriptor).long_name).read() };
unsafe { crate::ffi::table_text(long_name, DESCRIPTOR_TEXT_MAX_BYTES) }
.map(Utf8Bytes::from_static)
}
fn descriptor(self) -> *const ffmpeg_next::ffi::AVCodecDescriptor {
unsafe { crate::decoder::c_shims::avcodec_descriptor_get(self.0) }
}
pub const NONE: Self = Self(AVCodecID::AV_CODEC_ID_NONE as i32);
pub const H264: Self = Self(AVCodecID::AV_CODEC_ID_H264 as i32);
pub const HEVC: Self = Self(AVCodecID::AV_CODEC_ID_HEVC as i32);
pub const AV1: Self = Self(AVCodecID::AV_CODEC_ID_AV1 as i32);
pub const VP9: Self = Self(AVCodecID::AV_CODEC_ID_VP9 as i32);
pub const VP8: Self = Self(AVCodecID::AV_CODEC_ID_VP8 as i32);
pub const MPEG2VIDEO: Self = Self(AVCodecID::AV_CODEC_ID_MPEG2VIDEO as i32);
pub const MPEG4: Self = Self(AVCodecID::AV_CODEC_ID_MPEG4 as i32);
pub const PRORES: Self = Self(AVCodecID::AV_CODEC_ID_PRORES as i32);
pub const DNXHD: Self = Self(AVCodecID::AV_CODEC_ID_DNXHD as i32);
pub const FFV1: Self = Self(AVCodecID::AV_CODEC_ID_FFV1 as i32);
pub const JPEG2000: Self = Self(AVCodecID::AV_CODEC_ID_JPEG2000 as i32);
pub const MJPEG: Self = Self(AVCodecID::AV_CODEC_ID_MJPEG as i32);
pub const VC1: Self = Self(AVCodecID::AV_CODEC_ID_VC1 as i32);
pub const VVC: Self = Self(AVCodecID::AV_CODEC_ID_VVC as i32);
pub const AAC: Self = Self(AVCodecID::AV_CODEC_ID_AAC as i32);
pub const MP3: Self = Self(AVCodecID::AV_CODEC_ID_MP3 as i32);
pub const OPUS: Self = Self(AVCodecID::AV_CODEC_ID_OPUS as i32);
pub const FLAC: Self = Self(AVCodecID::AV_CODEC_ID_FLAC as i32);
pub const AC3: Self = Self(AVCodecID::AV_CODEC_ID_AC3 as i32);
pub const EAC3: Self = Self(AVCodecID::AV_CODEC_ID_EAC3 as i32);
pub const ALAC: Self = Self(AVCodecID::AV_CODEC_ID_ALAC as i32);
pub const DTS: Self = Self(AVCodecID::AV_CODEC_ID_DTS as i32);
pub const VORBIS: Self = Self(AVCodecID::AV_CODEC_ID_VORBIS as i32);
pub const PCM_S16LE: Self = Self(AVCodecID::AV_CODEC_ID_PCM_S16LE as i32);
pub const PCM_S16BE: Self = Self(AVCodecID::AV_CODEC_ID_PCM_S16BE as i32);
pub const PCM_S24LE: Self = Self(AVCodecID::AV_CODEC_ID_PCM_S24LE as i32);
pub const PCM_S32LE: Self = Self(AVCodecID::AV_CODEC_ID_PCM_S32LE as i32);
pub const PCM_F32LE: Self = Self(AVCodecID::AV_CODEC_ID_PCM_F32LE as i32);
pub const PCM_F64LE: Self = Self(AVCodecID::AV_CODEC_ID_PCM_F64LE as i32);
pub const SUBRIP: Self = Self(AVCodecID::AV_CODEC_ID_SUBRIP as i32);
pub const ASS: Self = Self(AVCodecID::AV_CODEC_ID_ASS as i32);
pub const WEBVTT: Self = Self(AVCodecID::AV_CODEC_ID_WEBVTT as i32);
pub const MOV_TEXT: Self = Self(AVCodecID::AV_CODEC_ID_MOV_TEXT as i32);
pub const DVB_SUBTITLE: Self = Self(AVCodecID::AV_CODEC_ID_DVB_SUBTITLE as i32);
pub const HDMV_PGS_SUBTITLE: Self = Self(AVCodecID::AV_CODEC_ID_HDMV_PGS_SUBTITLE as i32);
pub const DVD_SUBTITLE: Self = Self(AVCodecID::AV_CODEC_ID_DVD_SUBTITLE as i32);
}
impl fmt::Debug for CodecId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match *self {
Self::NONE => "NONE",
Self::H264 => "H264",
Self::HEVC => "HEVC",
Self::AV1 => "AV1",
Self::VP9 => "VP9",
Self::VP8 => "VP8",
Self::MPEG2VIDEO => "MPEG2VIDEO",
Self::MPEG4 => "MPEG4",
Self::PRORES => "PRORES",
Self::DNXHD => "DNXHD",
Self::FFV1 => "FFV1",
Self::JPEG2000 => "JPEG2000",
Self::MJPEG => "MJPEG",
Self::VC1 => "VC1",
Self::VVC => "VVC",
Self::AAC => "AAC",
Self::MP3 => "MP3",
Self::OPUS => "OPUS",
Self::FLAC => "FLAC",
Self::AC3 => "AC3",
Self::EAC3 => "EAC3",
Self::ALAC => "ALAC",
Self::DTS => "DTS",
Self::VORBIS => "VORBIS",
Self::PCM_S16LE => "PCM_S16LE",
Self::PCM_S16BE => "PCM_S16BE",
Self::PCM_S24LE => "PCM_S24LE",
Self::PCM_S32LE => "PCM_S32LE",
Self::PCM_F32LE => "PCM_F32LE",
Self::PCM_F64LE => "PCM_F64LE",
Self::SUBRIP => "SUBRIP",
Self::ASS => "ASS",
Self::WEBVTT => "WEBVTT",
Self::MOV_TEXT => "MOV_TEXT",
Self::DVB_SUBTITLE => "DVB_SUBTITLE",
Self::HDMV_PGS_SUBTITLE => "HDMV_PGS_SUBTITLE",
Self::DVD_SUBTITLE => "DVD_SUBTITLE",
_ => return write!(f, "CodecId({})", self.0),
};
write!(f, "CodecId::{name}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_raw_round_trips() {
let id = CodecId::from_raw(27);
assert_eq!(id.raw(), 27);
}
#[test]
fn known_constants_match_av_values() {
assert_eq!(CodecId::H264.raw(), AVCodecID::AV_CODEC_ID_H264 as i32);
assert_eq!(CodecId::AAC.raw(), AVCodecID::AV_CODEC_ID_AAC as i32);
assert_eq!(CodecId::SUBRIP.raw(), AVCodecID::AV_CODEC_ID_SUBRIP as i32);
}
#[test]
fn debug_uses_name_for_known_codecs() {
assert_eq!(format!("{:?}", CodecId::H264), "CodecId::H264");
assert_eq!(format!("{:?}", CodecId::AAC), "CodecId::AAC");
}
#[test]
fn debug_falls_back_to_raw_for_unknown() {
let unknown = CodecId::from_raw(-99_999);
assert_eq!(format!("{:?}", unknown), "CodecId(-99999)");
}
#[test]
fn equality_is_value_based() {
assert_eq!(CodecId::H264, CodecId::from_raw(CodecId::H264.raw()));
assert_ne!(CodecId::H264, CodecId::HEVC);
}
#[test]
fn known_codecs_name_themselves() {
assert_eq!(CodecId::H264.name().as_deref(), Some("h264"));
assert_eq!(CodecId::AAC.name().as_deref(), Some("aac"));
assert_eq!(CodecId::SUBRIP.name().as_deref(), Some("subrip"));
}
#[test]
fn a_known_codec_carries_a_description_too() {
let long = CodecId::H264.long_name().expect("h264 has a description");
assert!(
long.contains("H.264"),
"libavcodec's description for h264 should name the standard, got {long:?}",
);
}
#[test]
fn an_id_with_no_descriptor_has_no_name() {
for unknown in [CodecId::from_raw(-99_999), CodecId::from_raw(i32::MAX)] {
assert_eq!(unknown.name(), None, "{unknown:?} named itself");
assert_eq!(unknown.long_name(), None, "{unknown:?} described itself");
}
}
#[test]
fn the_none_sentinel_names_no_codec() {
assert_eq!(CodecId::NONE.name(), None);
}
#[test]
fn the_name_is_a_rendering_and_the_number_is_the_identity() {
let from_number = CodecId::from_raw(CodecId::H264.raw());
assert_eq!(from_number, CodecId::H264);
assert_eq!(from_number.name(), CodecId::H264.name());
assert_ne!(CodecId::H264.name(), CodecId::HEVC.name());
}
}