apple_cf/cm/
format_description.rs1#![allow(dead_code)]
4
5use crate::{
6 cf::{CFArray, CFData, CFDictionary},
7 ffi,
8};
9use std::{fmt, ops::Deref};
10
11pub struct CMFormatDescription(*mut std::ffi::c_void);
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct CMVideoParameterSets {
16 pub parameter_sets: Vec<Vec<u8>>,
17 pub nal_unit_header_length: i32,
18}
19
20impl PartialEq for CMFormatDescription {
21 fn eq(&self, other: &Self) -> bool {
22 self.0 == other.0
23 }
24}
25
26impl Eq for CMFormatDescription {}
27
28impl std::hash::Hash for CMFormatDescription {
29 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
30 unsafe {
31 let hash_value = ffi::cm_format_description_hash(self.0);
32 hash_value.hash(state);
33 }
34 }
35}
36
37pub mod media_types {
39 use crate::utils::four_char_code::FourCharCode;
40
41 pub const VIDEO: FourCharCode = FourCharCode::from_bytes(*b"vide");
43 pub const AUDIO: FourCharCode = FourCharCode::from_bytes(*b"soun");
45 pub const MUXED: FourCharCode = FourCharCode::from_bytes(*b"mux ");
47 pub const TEXT: FourCharCode = FourCharCode::from_bytes(*b"text");
49 pub const CLOSED_CAPTION: FourCharCode = FourCharCode::from_bytes(*b"clcp");
51 pub const METADATA: FourCharCode = FourCharCode::from_bytes(*b"meta");
53 pub const TIMECODE: FourCharCode = FourCharCode::from_bytes(*b"tmcd");
55}
56
57pub mod codec_types {
59 use crate::utils::four_char_code::FourCharCode;
60
61 pub const H264: FourCharCode = FourCharCode::from_bytes(*b"avc1");
64 pub const HEVC: FourCharCode = FourCharCode::from_bytes(*b"hvc1");
66 pub const HEVC_2: FourCharCode = FourCharCode::from_bytes(*b"hev1");
68 pub const JPEG: FourCharCode = FourCharCode::from_bytes(*b"jpeg");
70 pub const PRORES_422: FourCharCode = FourCharCode::from_bytes(*b"apcn");
72 pub const PRORES_4444: FourCharCode = FourCharCode::from_bytes(*b"ap4h");
74
75 pub const AAC: FourCharCode = FourCharCode::from_bytes(*b"aac ");
78 pub const LPCM: FourCharCode = FourCharCode::from_bytes(*b"lpcm");
80 pub const ALAC: FourCharCode = FourCharCode::from_bytes(*b"alac");
82 pub const OPUS: FourCharCode = FourCharCode::from_bytes(*b"opus");
84 pub const FLAC: FourCharCode = FourCharCode::from_bytes(*b"flac");
86}
87
88pub mod metadata_format_types {
90 use crate::utils::four_char_code::FourCharCode;
91
92 pub const ICY: FourCharCode = FourCharCode::from_bytes(*b"icy ");
94 pub const ID3: FourCharCode = FourCharCode::from_bytes(*b"id3 ");
96 pub const BOXED: FourCharCode = FourCharCode::from_bytes(*b"mebx");
98 pub const EMSG: FourCharCode = FourCharCode::from_bytes(*b"emsg");
100}
101
102macro_rules! cfstring_constant_fn {
103 ($vis:vis fn $name:ident => $ffi_name:ident) => {
104 #[must_use]
105 $vis fn $name() -> CFString {
106 let ptr = unsafe { ffi::$ffi_name() };
107 unsafe { CFString::from_raw(ptr) }
108 .expect(concat!(stringify!($ffi_name), " returned NULL"))
109 }
110 };
111}
112
113pub mod format_description_extension_keys {
115 use crate::{cf::CFString, ffi};
116
117 cfstring_constant_fn!(pub fn metadata_key_table => cm_metadata_format_description_extension_key_metadata_key_table);
118}
119
120pub mod metadata_description_keys {
122 use crate::{cf::CFString, ffi};
123
124 cfstring_constant_fn!(pub fn conforming_data_types => cm_metadata_format_description_key_conforming_data_types);
125 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_key_data_type);
126 cfstring_constant_fn!(pub fn data_type_namespace => cm_metadata_format_description_key_data_type_namespace);
127 cfstring_constant_fn!(pub fn language_tag => cm_metadata_format_description_key_language_tag);
128 cfstring_constant_fn!(pub fn local_id => cm_metadata_format_description_key_local_id);
129 cfstring_constant_fn!(pub fn namespace => cm_metadata_format_description_key_namespace);
130 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_key_setup_data);
131 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_key_structural_dependency);
132 cfstring_constant_fn!(pub fn value => cm_metadata_format_description_key_value);
133}
134
135pub mod metadata_specification_keys {
137 use crate::{cf::CFString, ffi};
138
139 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_metadata_specification_key_data_type);
140 cfstring_constant_fn!(pub fn extended_language_tag => cm_metadata_format_description_metadata_specification_key_extended_language_tag);
141 cfstring_constant_fn!(pub fn identifier => cm_metadata_format_description_metadata_specification_key_identifier);
142 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_metadata_specification_key_setup_data);
143 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_metadata_specification_key_structural_dependency);
144}
145
146pub mod metadata_structural_dependency_keys {
148 use crate::{cf::CFString, ffi};
149
150 cfstring_constant_fn!(pub fn dependency_is_invalid_flag => cm_metadata_format_description_structural_dependency_key_dependency_is_invalid_flag);
151}
152
153impl CMFormatDescription {
154 pub unsafe fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
162 if ptr.is_null() {
163 None
164 } else {
165 Some(Self(ptr))
166 }
167 }
168
169 #[must_use]
176 pub unsafe fn from_raw_borrowed(ptr: *mut std::ffi::c_void) -> Option<Self> {
177 if ptr.is_null() {
178 None
179 } else {
180 let retained = unsafe { ffi::cm_format_description_retain(ptr) };
181 unsafe { Self::from_raw(retained) }
182 }
183 }
184
185 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
191 Self(ptr)
192 }
193
194 #[must_use]
196 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
197 self.0
198 }
199
200 #[must_use]
202 pub fn media_type_raw(&self) -> u32 {
203 unsafe { ffi::cm_format_description_get_media_type(self.0) }
204 }
205
206 #[must_use]
208 pub fn media_type(&self) -> crate::utils::four_char_code::FourCharCode {
209 crate::utils::four_char_code::FourCharCode::from(self.media_type_raw())
210 }
211
212 #[must_use]
214 pub fn media_subtype_raw(&self) -> u32 {
215 unsafe { ffi::cm_format_description_get_media_subtype(self.0) }
216 }
217
218 #[must_use]
220 pub fn media_subtype(&self) -> crate::utils::four_char_code::FourCharCode {
221 crate::utils::four_char_code::FourCharCode::from(self.media_subtype_raw())
222 }
223
224 #[must_use]
226 pub fn extensions(&self) -> Option<*const std::ffi::c_void> {
227 unsafe {
228 let ptr = ffi::cm_format_description_get_extensions(self.0);
229 if ptr.is_null() {
230 None
231 } else {
232 Some(ptr)
233 }
234 }
235 }
236
237 #[must_use]
239 pub fn is_video(&self) -> bool {
240 self.media_type() == media_types::VIDEO
241 }
242
243 #[must_use]
245 pub fn is_audio(&self) -> bool {
246 self.media_type() == media_types::AUDIO
247 }
248
249 #[must_use]
251 pub fn is_muxed(&self) -> bool {
252 self.media_type() == media_types::MUXED
253 }
254
255 #[must_use]
257 pub fn is_text(&self) -> bool {
258 self.media_type() == media_types::TEXT
259 }
260
261 #[must_use]
263 pub fn is_closed_caption(&self) -> bool {
264 self.media_type() == media_types::CLOSED_CAPTION
265 }
266
267 #[must_use]
269 pub fn is_metadata(&self) -> bool {
270 self.media_type() == media_types::METADATA
271 }
272
273 #[must_use]
275 pub fn is_timecode(&self) -> bool {
276 self.media_type() == media_types::TIMECODE
277 }
278
279 #[must_use]
281 pub fn media_type_string(&self) -> String {
282 self.media_type().display()
283 }
284
285 #[must_use]
287 pub fn media_subtype_string(&self) -> String {
288 self.media_subtype().display()
289 }
290
291 #[must_use]
293 pub fn is_h264(&self) -> bool {
294 self.media_subtype() == codec_types::H264
295 }
296
297 #[must_use]
299 pub fn is_hevc(&self) -> bool {
300 let subtype = self.media_subtype();
301 subtype == codec_types::HEVC || subtype == codec_types::HEVC_2
302 }
303
304 #[must_use]
306 pub fn is_aac(&self) -> bool {
307 self.media_subtype() == codec_types::AAC
308 }
309
310 #[must_use]
312 pub fn is_pcm(&self) -> bool {
313 self.media_subtype() == codec_types::LPCM
314 }
315
316 #[must_use]
318 pub fn is_prores(&self) -> bool {
319 let subtype = self.media_subtype();
320 subtype == codec_types::PRORES_422 || subtype == codec_types::PRORES_4444
321 }
322
323 #[must_use]
325 pub fn is_alac(&self) -> bool {
326 self.media_subtype() == codec_types::ALAC
327 }
328
329 #[must_use]
330 pub fn video_dimensions(&self) -> Option<(i32, i32)> {
331 let mut width = 0_i32;
332 let mut height = 0_i32;
333 let is_video = unsafe {
334 ffi::acf_cm_video_format_description_get_dimensions(
335 self.0,
336 &raw mut width,
337 &raw mut height,
338 )
339 };
340 is_video.then_some((width, height))
341 }
342
343 #[allow(clippy::missing_errors_doc)]
344 pub fn video_parameter_sets(&self) -> Result<CMVideoParameterSets, i32> {
345 let hevc = if self.is_h264() {
346 false
347 } else if self.is_hevc() {
348 true
349 } else {
350 return Err(-12710);
351 };
352 let mut parameter_sets = Vec::new();
353 let mut nal_unit_header_length = 0_i32;
354 let mut count = 1_usize;
355 while parameter_sets.len() < count {
356 let mut status = 0_i32;
357 let ptr = unsafe {
358 ffi::acf_cm_video_format_description_copy_parameter_set(
359 self.0,
360 hevc,
361 parameter_sets.len(),
362 &raw mut count,
363 &raw mut nal_unit_header_length,
364 &raw mut status,
365 )
366 };
367 let data = unsafe { CFData::from_raw(ptr) };
368 if status != 0 {
369 return Err(status);
370 }
371 parameter_sets.push(data.ok_or(-12710)?.to_vec());
372 }
373 Ok(CMVideoParameterSets {
374 parameter_sets,
375 nal_unit_header_length,
376 })
377 }
378
379 #[must_use]
385 pub fn audio_sample_rate(&self) -> Option<f64> {
386 if !self.is_audio() {
387 return None;
388 }
389 let rate = unsafe { ffi::cm_format_description_get_audio_sample_rate(self.0) };
390 if rate > 0.0 {
391 Some(rate)
392 } else {
393 None
394 }
395 }
396
397 #[must_use]
401 pub fn audio_channel_count(&self) -> Option<u32> {
402 if !self.is_audio() {
403 return None;
404 }
405 let count = unsafe { ffi::cm_format_description_get_audio_channel_count(self.0) };
406 if count > 0 {
407 Some(count)
408 } else {
409 None
410 }
411 }
412
413 #[must_use]
417 pub fn audio_bits_per_channel(&self) -> Option<u32> {
418 if !self.is_audio() {
419 return None;
420 }
421 let bits = unsafe { ffi::cm_format_description_get_audio_bits_per_channel(self.0) };
422 if bits > 0 {
423 Some(bits)
424 } else {
425 None
426 }
427 }
428
429 #[must_use]
433 pub fn audio_bytes_per_frame(&self) -> Option<u32> {
434 if !self.is_audio() {
435 return None;
436 }
437 let bytes = unsafe { ffi::cm_format_description_get_audio_bytes_per_frame(self.0) };
438 if bytes > 0 {
439 Some(bytes)
440 } else {
441 None
442 }
443 }
444
445 #[must_use]
449 pub fn audio_format_flags(&self) -> Option<u32> {
450 if !self.is_audio() {
451 return None;
452 }
453 Some(unsafe { ffi::cm_format_description_get_audio_format_flags(self.0) })
454 }
455
456 #[must_use]
458 pub fn audio_is_float(&self) -> bool {
459 self.audio_format_flags().is_some_and(|f| f & 1 != 0)
461 }
462
463 #[must_use]
465 pub fn audio_is_big_endian(&self) -> bool {
466 self.audio_format_flags().is_some_and(|f| f & 2 != 0)
468 }
469}
470
471crate::utils::retained::cf_retained!(
472 CMFormatDescription,
473 retain = ffi::cm_format_description_retain,
474 release = ffi::cm_format_description_release,
475 drop = unchecked,
476);
477
478unsafe impl Send for CMFormatDescription {}
482unsafe impl Sync for CMFormatDescription {}
483
484impl fmt::Debug for CMFormatDescription {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486 f.debug_struct("CMFormatDescription")
487 .field("media_type", &self.media_type_string())
488 .field("codec", &self.media_subtype_string())
489 .finish()
490 }
491}
492
493impl fmt::Display for CMFormatDescription {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 write!(
496 f,
497 "CMFormatDescription(type: 0x{:08X}, subtype: 0x{:08X})",
498 self.media_type_raw(),
499 self.media_subtype_raw()
500 )
501 }
502}
503
504#[derive(Clone, PartialEq, Eq, Hash)]
506pub struct CMMetadataFormatDescription(CMFormatDescription);
507
508impl CMMetadataFormatDescription {
509 #[must_use]
517 pub unsafe fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
518 unsafe { CMFormatDescription::from_raw(ptr) }.map(Self)
519 }
520
521 #[must_use]
528 pub unsafe fn from_raw_borrowed(ptr: *mut std::ffi::c_void) -> Option<Self> {
529 unsafe { CMFormatDescription::from_raw_borrowed(ptr) }.map(Self)
530 }
531
532 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
538 Self(unsafe { CMFormatDescription::from_ptr(ptr) })
539 }
540
541 #[must_use]
543 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
544 self.0.as_ptr()
545 }
546
547 #[must_use]
549 pub const fn as_format_description(&self) -> &CMFormatDescription {
550 &self.0
551 }
552
553 #[must_use]
555 pub fn into_format_description(self) -> CMFormatDescription {
556 self.0
557 }
558
559 pub fn create_with_keys(
565 metadata_type: crate::utils::four_char_code::FourCharCode,
566 keys: Option<&CFArray>,
567 ) -> Result<Self, i32> {
568 let mut ptr = std::ptr::null_mut();
569 let status = unsafe {
570 ffi::cm_metadata_format_description_create_with_keys(
571 metadata_type.into(),
572 keys.map_or(std::ptr::null_mut(), CFArray::as_ptr),
573 &raw mut ptr,
574 )
575 };
576 if status == 0 && !ptr.is_null() {
577 unsafe { Self::from_raw(ptr) }.ok_or(status)
578 } else {
579 Err(status)
580 }
581 }
582
583 pub fn create_with_metadata_specifications(
589 metadata_type: crate::utils::four_char_code::FourCharCode,
590 metadata_specifications: &CFArray,
591 ) -> Result<Self, i32> {
592 let mut ptr = std::ptr::null_mut();
593 let status = unsafe {
594 ffi::cm_metadata_format_description_create_with_metadata_specifications(
595 metadata_type.into(),
596 metadata_specifications.as_ptr(),
597 &raw mut ptr,
598 )
599 };
600 if status == 0 && !ptr.is_null() {
601 unsafe { Self::from_raw(ptr) }.ok_or(status)
602 } else {
603 Err(status)
604 }
605 }
606
607 pub fn extend_with_metadata_specifications(
613 &self,
614 metadata_specifications: &CFArray,
615 ) -> Result<Self, i32> {
616 let mut ptr = std::ptr::null_mut();
617 let status = unsafe {
618 ffi::cm_metadata_format_description_create_with_description_and_metadata_specifications(
619 self.as_ptr(),
620 metadata_specifications.as_ptr(),
621 &raw mut ptr,
622 )
623 };
624 if status == 0 && !ptr.is_null() {
625 unsafe { Self::from_raw(ptr) }.ok_or(status)
626 } else {
627 Err(status)
628 }
629 }
630
631 pub fn merge(&self, other: &Self) -> Result<Self, i32> {
637 let mut ptr = std::ptr::null_mut();
638 let status = unsafe {
639 ffi::cm_metadata_format_description_create_by_merging_descriptions(
640 self.as_ptr(),
641 other.as_ptr(),
642 &raw mut ptr,
643 )
644 };
645 if status == 0 && !ptr.is_null() {
646 unsafe { Self::from_raw(ptr) }.ok_or(status)
647 } else {
648 Err(status)
649 }
650 }
651
652 #[must_use]
654 pub fn identifiers(&self) -> Option<CFArray> {
655 let ptr = unsafe { ffi::cm_metadata_format_description_get_identifiers(self.as_ptr()) };
656 unsafe { CFArray::from_raw(ptr) }
657 }
658
659 #[must_use]
661 pub fn key_with_local_id(&self, local_id: u32) -> Option<CFDictionary> {
662 let ptr = unsafe {
663 ffi::cm_metadata_format_description_get_key_with_local_id(self.as_ptr(), local_id)
664 };
665 unsafe { CFDictionary::from_raw(ptr) }
666 }
667}
668
669impl Deref for CMMetadataFormatDescription {
670 type Target = CMFormatDescription;
671
672 fn deref(&self) -> &Self::Target {
673 &self.0
674 }
675}
676
677impl TryFrom<CMFormatDescription> for CMMetadataFormatDescription {
678 type Error = CMFormatDescription;
679
680 fn try_from(value: CMFormatDescription) -> Result<Self, Self::Error> {
681 if value.is_metadata() {
682 Ok(Self(value))
683 } else {
684 Err(value)
685 }
686 }
687}
688
689impl From<CMMetadataFormatDescription> for CMFormatDescription {
690 fn from(value: CMMetadataFormatDescription) -> Self {
691 value.0
692 }
693}
694
695impl fmt::Debug for CMMetadataFormatDescription {
696 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
697 f.debug_struct("CMMetadataFormatDescription")
698 .field("media_type", &self.media_type_string())
699 .field("codec", &self.media_subtype_string())
700 .finish()
701 }
702}
703
704impl fmt::Display for CMMetadataFormatDescription {
705 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706 fmt::Display::fmt(&self.0, f)
707 }
708}