#![allow(dead_code)]
use crate::{
cf::{CFArray, CFDictionary},
ffi,
};
use std::{fmt, ops::Deref};
pub struct CMFormatDescription(*mut std::ffi::c_void);
impl PartialEq for CMFormatDescription {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for CMFormatDescription {}
impl std::hash::Hash for CMFormatDescription {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe {
let hash_value = ffi::cm_format_description_hash(self.0);
hash_value.hash(state);
}
}
}
pub mod media_types {
use crate::utils::four_char_code::FourCharCode;
pub const VIDEO: FourCharCode = FourCharCode::from_bytes(*b"vide");
pub const AUDIO: FourCharCode = FourCharCode::from_bytes(*b"soun");
pub const MUXED: FourCharCode = FourCharCode::from_bytes(*b"mux ");
pub const TEXT: FourCharCode = FourCharCode::from_bytes(*b"text");
pub const CLOSED_CAPTION: FourCharCode = FourCharCode::from_bytes(*b"clcp");
pub const METADATA: FourCharCode = FourCharCode::from_bytes(*b"meta");
pub const TIMECODE: FourCharCode = FourCharCode::from_bytes(*b"tmcd");
}
pub mod codec_types {
use crate::utils::four_char_code::FourCharCode;
pub const H264: FourCharCode = FourCharCode::from_bytes(*b"avc1");
pub const HEVC: FourCharCode = FourCharCode::from_bytes(*b"hvc1");
pub const HEVC_2: FourCharCode = FourCharCode::from_bytes(*b"hev1");
pub const JPEG: FourCharCode = FourCharCode::from_bytes(*b"jpeg");
pub const PRORES_422: FourCharCode = FourCharCode::from_bytes(*b"apcn");
pub const PRORES_4444: FourCharCode = FourCharCode::from_bytes(*b"ap4h");
pub const AAC: FourCharCode = FourCharCode::from_bytes(*b"aac ");
pub const LPCM: FourCharCode = FourCharCode::from_bytes(*b"lpcm");
pub const ALAC: FourCharCode = FourCharCode::from_bytes(*b"alac");
pub const OPUS: FourCharCode = FourCharCode::from_bytes(*b"opus");
pub const FLAC: FourCharCode = FourCharCode::from_bytes(*b"flac");
}
pub mod metadata_format_types {
use crate::utils::four_char_code::FourCharCode;
pub const ICY: FourCharCode = FourCharCode::from_bytes(*b"icy ");
pub const ID3: FourCharCode = FourCharCode::from_bytes(*b"id3 ");
pub const BOXED: FourCharCode = FourCharCode::from_bytes(*b"mebx");
pub const EMSG: FourCharCode = FourCharCode::from_bytes(*b"emsg");
}
macro_rules! cfstring_constant_fn {
($vis:vis fn $name:ident => $ffi_name:ident) => {
#[must_use]
$vis fn $name() -> CFString {
let ptr = unsafe { ffi::$ffi_name() };
CFString::from_raw(ptr).expect(concat!(stringify!($ffi_name), " returned NULL"))
}
};
}
pub mod format_description_extension_keys {
use crate::{cf::CFString, ffi};
cfstring_constant_fn!(pub fn metadata_key_table => cm_metadata_format_description_extension_key_metadata_key_table);
}
pub mod metadata_description_keys {
use crate::{cf::CFString, ffi};
cfstring_constant_fn!(pub fn conforming_data_types => cm_metadata_format_description_key_conforming_data_types);
cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_key_data_type);
cfstring_constant_fn!(pub fn data_type_namespace => cm_metadata_format_description_key_data_type_namespace);
cfstring_constant_fn!(pub fn language_tag => cm_metadata_format_description_key_language_tag);
cfstring_constant_fn!(pub fn local_id => cm_metadata_format_description_key_local_id);
cfstring_constant_fn!(pub fn namespace => cm_metadata_format_description_key_namespace);
cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_key_setup_data);
cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_key_structural_dependency);
cfstring_constant_fn!(pub fn value => cm_metadata_format_description_key_value);
}
pub mod metadata_specification_keys {
use crate::{cf::CFString, ffi};
cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_metadata_specification_key_data_type);
cfstring_constant_fn!(pub fn extended_language_tag => cm_metadata_format_description_metadata_specification_key_extended_language_tag);
cfstring_constant_fn!(pub fn identifier => cm_metadata_format_description_metadata_specification_key_identifier);
cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_metadata_specification_key_setup_data);
cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_metadata_specification_key_structural_dependency);
}
pub mod metadata_structural_dependency_keys {
use crate::{cf::CFString, ffi};
cfstring_constant_fn!(pub fn dependency_is_invalid_flag => cm_metadata_format_description_structural_dependency_key_dependency_is_invalid_flag);
}
impl CMFormatDescription {
pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self(ptr))
}
}
pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
Self(ptr)
}
#[must_use]
pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
self.0
}
#[must_use]
pub fn media_type_raw(&self) -> u32 {
unsafe { ffi::cm_format_description_get_media_type(self.0) }
}
#[must_use]
pub fn media_type(&self) -> crate::utils::four_char_code::FourCharCode {
crate::utils::four_char_code::FourCharCode::from(self.media_type_raw())
}
#[must_use]
pub fn media_subtype_raw(&self) -> u32 {
unsafe { ffi::cm_format_description_get_media_subtype(self.0) }
}
#[must_use]
pub fn media_subtype(&self) -> crate::utils::four_char_code::FourCharCode {
crate::utils::four_char_code::FourCharCode::from(self.media_subtype_raw())
}
#[must_use]
pub fn extensions(&self) -> Option<*const std::ffi::c_void> {
unsafe {
let ptr = ffi::cm_format_description_get_extensions(self.0);
if ptr.is_null() {
None
} else {
Some(ptr)
}
}
}
#[must_use]
pub fn is_video(&self) -> bool {
self.media_type() == media_types::VIDEO
}
#[must_use]
pub fn is_audio(&self) -> bool {
self.media_type() == media_types::AUDIO
}
#[must_use]
pub fn is_muxed(&self) -> bool {
self.media_type() == media_types::MUXED
}
#[must_use]
pub fn is_text(&self) -> bool {
self.media_type() == media_types::TEXT
}
#[must_use]
pub fn is_closed_caption(&self) -> bool {
self.media_type() == media_types::CLOSED_CAPTION
}
#[must_use]
pub fn is_metadata(&self) -> bool {
self.media_type() == media_types::METADATA
}
#[must_use]
pub fn is_timecode(&self) -> bool {
self.media_type() == media_types::TIMECODE
}
#[must_use]
pub fn media_type_string(&self) -> String {
self.media_type().display()
}
#[must_use]
pub fn media_subtype_string(&self) -> String {
self.media_subtype().display()
}
#[must_use]
pub fn is_h264(&self) -> bool {
self.media_subtype() == codec_types::H264
}
#[must_use]
pub fn is_hevc(&self) -> bool {
let subtype = self.media_subtype();
subtype == codec_types::HEVC || subtype == codec_types::HEVC_2
}
#[must_use]
pub fn is_aac(&self) -> bool {
self.media_subtype() == codec_types::AAC
}
#[must_use]
pub fn is_pcm(&self) -> bool {
self.media_subtype() == codec_types::LPCM
}
#[must_use]
pub fn is_prores(&self) -> bool {
let subtype = self.media_subtype();
subtype == codec_types::PRORES_422 || subtype == codec_types::PRORES_4444
}
#[must_use]
pub fn is_alac(&self) -> bool {
self.media_subtype() == codec_types::ALAC
}
#[must_use]
pub fn audio_sample_rate(&self) -> Option<f64> {
if !self.is_audio() {
return None;
}
let rate = unsafe { ffi::cm_format_description_get_audio_sample_rate(self.0) };
if rate > 0.0 {
Some(rate)
} else {
None
}
}
#[must_use]
pub fn audio_channel_count(&self) -> Option<u32> {
if !self.is_audio() {
return None;
}
let count = unsafe { ffi::cm_format_description_get_audio_channel_count(self.0) };
if count > 0 {
Some(count)
} else {
None
}
}
#[must_use]
pub fn audio_bits_per_channel(&self) -> Option<u32> {
if !self.is_audio() {
return None;
}
let bits = unsafe { ffi::cm_format_description_get_audio_bits_per_channel(self.0) };
if bits > 0 {
Some(bits)
} else {
None
}
}
#[must_use]
pub fn audio_bytes_per_frame(&self) -> Option<u32> {
if !self.is_audio() {
return None;
}
let bytes = unsafe { ffi::cm_format_description_get_audio_bytes_per_frame(self.0) };
if bytes > 0 {
Some(bytes)
} else {
None
}
}
#[must_use]
pub fn audio_format_flags(&self) -> Option<u32> {
if !self.is_audio() {
return None;
}
Some(unsafe { ffi::cm_format_description_get_audio_format_flags(self.0) })
}
#[must_use]
pub fn audio_is_float(&self) -> bool {
self.audio_format_flags().is_some_and(|f| f & 1 != 0)
}
#[must_use]
pub fn audio_is_big_endian(&self) -> bool {
self.audio_format_flags().is_some_and(|f| f & 2 != 0)
}
}
impl Clone for CMFormatDescription {
fn clone(&self) -> Self {
unsafe {
let ptr = ffi::cm_format_description_retain(self.0);
Self(ptr)
}
}
}
impl Drop for CMFormatDescription {
fn drop(&mut self) {
unsafe {
ffi::cm_format_description_release(self.0);
}
}
}
unsafe impl Send for CMFormatDescription {}
unsafe impl Sync for CMFormatDescription {}
impl fmt::Debug for CMFormatDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CMFormatDescription")
.field("media_type", &self.media_type_string())
.field("codec", &self.media_subtype_string())
.finish()
}
}
impl fmt::Display for CMFormatDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CMFormatDescription(type: 0x{:08X}, subtype: 0x{:08X})",
self.media_type_raw(),
self.media_subtype_raw()
)
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct CMMetadataFormatDescription(CMFormatDescription);
impl CMMetadataFormatDescription {
#[must_use]
pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
CMFormatDescription::from_raw(ptr).map(Self)
}
pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
Self(CMFormatDescription::from_ptr(ptr))
}
#[must_use]
pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
self.0.as_ptr()
}
#[must_use]
pub const fn as_format_description(&self) -> &CMFormatDescription {
&self.0
}
#[must_use]
pub fn into_format_description(self) -> CMFormatDescription {
self.0
}
pub fn create_with_keys(
metadata_type: crate::utils::four_char_code::FourCharCode,
keys: Option<&CFArray>,
) -> Result<Self, i32> {
let mut ptr = std::ptr::null_mut();
let status = unsafe {
ffi::cm_metadata_format_description_create_with_keys(
metadata_type.into(),
keys.map_or(std::ptr::null_mut(), CFArray::as_ptr),
&mut ptr,
)
};
if status == 0 && !ptr.is_null() {
Self::from_raw(ptr).ok_or(status)
} else {
Err(status)
}
}
pub fn create_with_metadata_specifications(
metadata_type: crate::utils::four_char_code::FourCharCode,
metadata_specifications: &CFArray,
) -> Result<Self, i32> {
let mut ptr = std::ptr::null_mut();
let status = unsafe {
ffi::cm_metadata_format_description_create_with_metadata_specifications(
metadata_type.into(),
metadata_specifications.as_ptr(),
&mut ptr,
)
};
if status == 0 && !ptr.is_null() {
Self::from_raw(ptr).ok_or(status)
} else {
Err(status)
}
}
pub fn extend_with_metadata_specifications(
&self,
metadata_specifications: &CFArray,
) -> Result<Self, i32> {
let mut ptr = std::ptr::null_mut();
let status = unsafe {
ffi::cm_metadata_format_description_create_with_description_and_metadata_specifications(
self.as_ptr(),
metadata_specifications.as_ptr(),
&mut ptr,
)
};
if status == 0 && !ptr.is_null() {
Self::from_raw(ptr).ok_or(status)
} else {
Err(status)
}
}
pub fn merge(&self, other: &Self) -> Result<Self, i32> {
let mut ptr = std::ptr::null_mut();
let status = unsafe {
ffi::cm_metadata_format_description_create_by_merging_descriptions(
self.as_ptr(),
other.as_ptr(),
&mut ptr,
)
};
if status == 0 && !ptr.is_null() {
Self::from_raw(ptr).ok_or(status)
} else {
Err(status)
}
}
#[must_use]
pub fn identifiers(&self) -> Option<CFArray> {
let ptr = unsafe { ffi::cm_metadata_format_description_get_identifiers(self.as_ptr()) };
CFArray::from_raw(ptr)
}
#[must_use]
pub fn key_with_local_id(&self, local_id: u32) -> Option<CFDictionary> {
let ptr = unsafe {
ffi::cm_metadata_format_description_get_key_with_local_id(self.as_ptr(), local_id)
};
CFDictionary::from_raw(ptr)
}
}
impl Deref for CMMetadataFormatDescription {
type Target = CMFormatDescription;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<CMFormatDescription> for CMMetadataFormatDescription {
type Error = CMFormatDescription;
fn try_from(value: CMFormatDescription) -> Result<Self, Self::Error> {
if value.is_metadata() {
Ok(Self(value))
} else {
Err(value)
}
}
}
impl From<CMMetadataFormatDescription> for CMFormatDescription {
fn from(value: CMMetadataFormatDescription) -> Self {
value.0
}
}
impl fmt::Debug for CMMetadataFormatDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CMMetadataFormatDescription")
.field("media_type", &self.media_type_string())
.field("codec", &self.media_subtype_string())
.finish()
}
}
impl fmt::Display for CMMetadataFormatDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}