use core::{fmt, str::FromStr};
use smol_bytes::Utf8Bytes;
const FORMAT_TEXT_MAX_BYTES: usize = 1024;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ContainerFormat {
name: Utf8Bytes,
long_name: Option<Utf8Bytes>,
}
impl ContainerFormat {
#[inline]
pub const fn new(name: Utf8Bytes, long_name: Option<Utf8Bytes>) -> Self {
Self { name, long_name }
}
#[inline]
pub fn name(&self) -> &str {
self.name.as_str()
}
#[inline]
pub fn long_name(&self) -> Option<&str> {
self.long_name.as_deref()
}
pub fn names(&self) -> impl Iterator<Item = &str> {
self.name.split(',').filter(|word| !word.is_empty())
}
#[inline]
pub fn is(&self, demuxer: DemuxerName) -> bool {
self.name() == demuxer.as_str()
}
#[doc(hidden)]
pub unsafe fn from_context(context: *const ffmpeg_next::ffi::AVFormatContext) -> Option<Self> {
if context.is_null() {
return None;
}
let iformat = unsafe { (*context).iformat };
if iformat.is_null() {
return None;
}
let (name, long_name) = unsafe { ((*iformat).name, (*iformat).long_name) };
let name =
Utf8Bytes::from_static(unsafe { crate::ffi::table_text(name, FORMAT_TEXT_MAX_BYTES) }?);
let long_name = unsafe { crate::ffi::table_text(long_name, FORMAT_TEXT_MAX_BYTES) }
.map(Utf8Bytes::from_static);
Some(Self::new(name, long_name))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DemuxerName(&'static str);
impl DemuxerName {
pub fn registered(name: &str) -> Result<Self, UnregisteredDemuxer> {
let mut word_of = None;
for registered in registered_demuxers() {
if registered == name {
return Ok(Self(registered));
}
if word_of.is_none() && registered.split(',').any(|word| word == name) {
word_of = Some(Self(registered));
}
}
Err(UnregisteredDemuxer {
name: Utf8Bytes::from(name),
word_of,
})
}
#[inline]
pub const fn as_str(&self) -> &'static str {
self.0
}
}
impl fmt::Display for DemuxerName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
impl AsRef<str> for DemuxerName {
#[inline]
fn as_ref(&self) -> &str {
self.0
}
}
impl FromStr for DemuxerName {
type Err = UnregisteredDemuxer;
fn from_str(name: &str) -> Result<Self, Self::Err> {
Self::registered(name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UnregisteredDemuxer {
name: Utf8Bytes,
word_of: Option<DemuxerName>,
}
impl UnregisteredDemuxer {
#[inline]
pub fn name(&self) -> &str {
self.name.as_str()
}
#[inline]
pub const fn word_of(&self) -> Option<DemuxerName> {
self.word_of
}
}
impl fmt::Display for UnregisteredDemuxer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"no demuxer the linked FFmpeg registers is named {:?}",
self.name()
)?;
match self.word_of {
Some(whole) => write!(
f,
": it is a word of {:?}, the demuxer's whole name",
whole.0
),
None => Ok(()),
}
}
}
impl core::error::Error for UnregisteredDemuxer {}
fn registered_demuxers() -> impl Iterator<Item = &'static str> {
let mut opaque = core::ptr::null_mut();
core::iter::from_fn(move || {
loop {
let demuxer = unsafe { ffmpeg_next::ffi::av_demuxer_iterate(&mut opaque) };
if demuxer.is_null() {
return None;
}
if let Some(name) = unsafe { crate::ffi::table_text((*demuxer).name, FORMAT_TEXT_MAX_BYTES) }
{
return Some(name);
}
}
})
}
#[cfg(test)]
mod tests;