apple_cf/cm/
format_description.rs1#![allow(dead_code)]
4
5use crate::{
6 cf::{CFArray, CFDictionary},
7 ffi,
8};
9use std::{fmt, ops::Deref};
10
11pub struct CMFormatDescription(*mut std::ffi::c_void);
13
14impl PartialEq for CMFormatDescription {
15 fn eq(&self, other: &Self) -> bool {
16 self.0 == other.0
17 }
18}
19
20impl Eq for CMFormatDescription {}
21
22impl std::hash::Hash for CMFormatDescription {
23 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
24 unsafe {
25 let hash_value = ffi::cm_format_description_hash(self.0);
26 hash_value.hash(state);
27 }
28 }
29}
30
31pub mod media_types {
33 use crate::utils::four_char_code::FourCharCode;
34
35 pub const VIDEO: FourCharCode = FourCharCode::from_bytes(*b"vide");
37 pub const AUDIO: FourCharCode = FourCharCode::from_bytes(*b"soun");
39 pub const MUXED: FourCharCode = FourCharCode::from_bytes(*b"mux ");
41 pub const TEXT: FourCharCode = FourCharCode::from_bytes(*b"text");
43 pub const CLOSED_CAPTION: FourCharCode = FourCharCode::from_bytes(*b"clcp");
45 pub const METADATA: FourCharCode = FourCharCode::from_bytes(*b"meta");
47 pub const TIMECODE: FourCharCode = FourCharCode::from_bytes(*b"tmcd");
49}
50
51pub mod codec_types {
53 use crate::utils::four_char_code::FourCharCode;
54
55 pub const H264: FourCharCode = FourCharCode::from_bytes(*b"avc1");
58 pub const HEVC: FourCharCode = FourCharCode::from_bytes(*b"hvc1");
60 pub const HEVC_2: FourCharCode = FourCharCode::from_bytes(*b"hev1");
62 pub const JPEG: FourCharCode = FourCharCode::from_bytes(*b"jpeg");
64 pub const PRORES_422: FourCharCode = FourCharCode::from_bytes(*b"apcn");
66 pub const PRORES_4444: FourCharCode = FourCharCode::from_bytes(*b"ap4h");
68
69 pub const AAC: FourCharCode = FourCharCode::from_bytes(*b"aac ");
72 pub const LPCM: FourCharCode = FourCharCode::from_bytes(*b"lpcm");
74 pub const ALAC: FourCharCode = FourCharCode::from_bytes(*b"alac");
76 pub const OPUS: FourCharCode = FourCharCode::from_bytes(*b"opus");
78 pub const FLAC: FourCharCode = FourCharCode::from_bytes(*b"flac");
80}
81
82pub mod metadata_format_types {
84 use crate::utils::four_char_code::FourCharCode;
85
86 pub const ICY: FourCharCode = FourCharCode::from_bytes(*b"icy ");
88 pub const ID3: FourCharCode = FourCharCode::from_bytes(*b"id3 ");
90 pub const BOXED: FourCharCode = FourCharCode::from_bytes(*b"mebx");
92 pub const EMSG: FourCharCode = FourCharCode::from_bytes(*b"emsg");
94}
95
96macro_rules! cfstring_constant_fn {
97 ($vis:vis fn $name:ident => $ffi_name:ident) => {
98 #[must_use]
99 $vis fn $name() -> CFString {
100 let ptr = unsafe { ffi::$ffi_name() };
101 CFString::from_raw(ptr).expect(concat!(stringify!($ffi_name), " returned NULL"))
102 }
103 };
104}
105
106pub mod format_description_extension_keys {
108 use crate::{cf::CFString, ffi};
109
110 cfstring_constant_fn!(pub fn metadata_key_table => cm_metadata_format_description_extension_key_metadata_key_table);
111}
112
113pub mod metadata_description_keys {
115 use crate::{cf::CFString, ffi};
116
117 cfstring_constant_fn!(pub fn conforming_data_types => cm_metadata_format_description_key_conforming_data_types);
118 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_key_data_type);
119 cfstring_constant_fn!(pub fn data_type_namespace => cm_metadata_format_description_key_data_type_namespace);
120 cfstring_constant_fn!(pub fn language_tag => cm_metadata_format_description_key_language_tag);
121 cfstring_constant_fn!(pub fn local_id => cm_metadata_format_description_key_local_id);
122 cfstring_constant_fn!(pub fn namespace => cm_metadata_format_description_key_namespace);
123 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_key_setup_data);
124 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_key_structural_dependency);
125 cfstring_constant_fn!(pub fn value => cm_metadata_format_description_key_value);
126}
127
128pub mod metadata_specification_keys {
130 use crate::{cf::CFString, ffi};
131
132 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_metadata_specification_key_data_type);
133 cfstring_constant_fn!(pub fn extended_language_tag => cm_metadata_format_description_metadata_specification_key_extended_language_tag);
134 cfstring_constant_fn!(pub fn identifier => cm_metadata_format_description_metadata_specification_key_identifier);
135 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_metadata_specification_key_setup_data);
136 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_metadata_specification_key_structural_dependency);
137}
138
139pub mod metadata_structural_dependency_keys {
141 use crate::{cf::CFString, ffi};
142
143 cfstring_constant_fn!(pub fn dependency_is_invalid_flag => cm_metadata_format_description_structural_dependency_key_dependency_is_invalid_flag);
144}
145
146impl CMFormatDescription {
147 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
149 if ptr.is_null() {
150 None
151 } else {
152 Some(Self(ptr))
153 }
154 }
155
156 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
161 Self(ptr)
162 }
163
164 #[must_use]
166 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
167 self.0
168 }
169
170 #[must_use]
172 pub fn media_type_raw(&self) -> u32 {
173 unsafe { ffi::cm_format_description_get_media_type(self.0) }
174 }
175
176 #[must_use]
178 pub fn media_type(&self) -> crate::utils::four_char_code::FourCharCode {
179 crate::utils::four_char_code::FourCharCode::from(self.media_type_raw())
180 }
181
182 #[must_use]
184 pub fn media_subtype_raw(&self) -> u32 {
185 unsafe { ffi::cm_format_description_get_media_subtype(self.0) }
186 }
187
188 #[must_use]
190 pub fn media_subtype(&self) -> crate::utils::four_char_code::FourCharCode {
191 crate::utils::four_char_code::FourCharCode::from(self.media_subtype_raw())
192 }
193
194 #[must_use]
196 pub fn extensions(&self) -> Option<*const std::ffi::c_void> {
197 unsafe {
198 let ptr = ffi::cm_format_description_get_extensions(self.0);
199 if ptr.is_null() {
200 None
201 } else {
202 Some(ptr)
203 }
204 }
205 }
206
207 #[must_use]
209 pub fn is_video(&self) -> bool {
210 self.media_type() == media_types::VIDEO
211 }
212
213 #[must_use]
215 pub fn is_audio(&self) -> bool {
216 self.media_type() == media_types::AUDIO
217 }
218
219 #[must_use]
221 pub fn is_muxed(&self) -> bool {
222 self.media_type() == media_types::MUXED
223 }
224
225 #[must_use]
227 pub fn is_text(&self) -> bool {
228 self.media_type() == media_types::TEXT
229 }
230
231 #[must_use]
233 pub fn is_closed_caption(&self) -> bool {
234 self.media_type() == media_types::CLOSED_CAPTION
235 }
236
237 #[must_use]
239 pub fn is_metadata(&self) -> bool {
240 self.media_type() == media_types::METADATA
241 }
242
243 #[must_use]
245 pub fn is_timecode(&self) -> bool {
246 self.media_type() == media_types::TIMECODE
247 }
248
249 #[must_use]
251 pub fn media_type_string(&self) -> String {
252 self.media_type().display()
253 }
254
255 #[must_use]
257 pub fn media_subtype_string(&self) -> String {
258 self.media_subtype().display()
259 }
260
261 #[must_use]
263 pub fn is_h264(&self) -> bool {
264 self.media_subtype() == codec_types::H264
265 }
266
267 #[must_use]
269 pub fn is_hevc(&self) -> bool {
270 let subtype = self.media_subtype();
271 subtype == codec_types::HEVC || subtype == codec_types::HEVC_2
272 }
273
274 #[must_use]
276 pub fn is_aac(&self) -> bool {
277 self.media_subtype() == codec_types::AAC
278 }
279
280 #[must_use]
282 pub fn is_pcm(&self) -> bool {
283 self.media_subtype() == codec_types::LPCM
284 }
285
286 #[must_use]
288 pub fn is_prores(&self) -> bool {
289 let subtype = self.media_subtype();
290 subtype == codec_types::PRORES_422 || subtype == codec_types::PRORES_4444
291 }
292
293 #[must_use]
295 pub fn is_alac(&self) -> bool {
296 self.media_subtype() == codec_types::ALAC
297 }
298
299 #[must_use]
305 pub fn audio_sample_rate(&self) -> Option<f64> {
306 if !self.is_audio() {
307 return None;
308 }
309 let rate = unsafe { ffi::cm_format_description_get_audio_sample_rate(self.0) };
310 if rate > 0.0 {
311 Some(rate)
312 } else {
313 None
314 }
315 }
316
317 #[must_use]
321 pub fn audio_channel_count(&self) -> Option<u32> {
322 if !self.is_audio() {
323 return None;
324 }
325 let count = unsafe { ffi::cm_format_description_get_audio_channel_count(self.0) };
326 if count > 0 {
327 Some(count)
328 } else {
329 None
330 }
331 }
332
333 #[must_use]
337 pub fn audio_bits_per_channel(&self) -> Option<u32> {
338 if !self.is_audio() {
339 return None;
340 }
341 let bits = unsafe { ffi::cm_format_description_get_audio_bits_per_channel(self.0) };
342 if bits > 0 {
343 Some(bits)
344 } else {
345 None
346 }
347 }
348
349 #[must_use]
353 pub fn audio_bytes_per_frame(&self) -> Option<u32> {
354 if !self.is_audio() {
355 return None;
356 }
357 let bytes = unsafe { ffi::cm_format_description_get_audio_bytes_per_frame(self.0) };
358 if bytes > 0 {
359 Some(bytes)
360 } else {
361 None
362 }
363 }
364
365 #[must_use]
369 pub fn audio_format_flags(&self) -> Option<u32> {
370 if !self.is_audio() {
371 return None;
372 }
373 Some(unsafe { ffi::cm_format_description_get_audio_format_flags(self.0) })
374 }
375
376 #[must_use]
378 pub fn audio_is_float(&self) -> bool {
379 self.audio_format_flags().is_some_and(|f| f & 1 != 0)
381 }
382
383 #[must_use]
385 pub fn audio_is_big_endian(&self) -> bool {
386 self.audio_format_flags().is_some_and(|f| f & 2 != 0)
388 }
389}
390
391impl Clone for CMFormatDescription {
392 fn clone(&self) -> Self {
393 unsafe {
394 let ptr = ffi::cm_format_description_retain(self.0);
395 Self(ptr)
396 }
397 }
398}
399
400impl Drop for CMFormatDescription {
401 fn drop(&mut self) {
402 unsafe {
403 ffi::cm_format_description_release(self.0);
404 }
405 }
406}
407
408unsafe impl Send for CMFormatDescription {}
412unsafe impl Sync for CMFormatDescription {}
413
414impl fmt::Debug for CMFormatDescription {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416 f.debug_struct("CMFormatDescription")
417 .field("media_type", &self.media_type_string())
418 .field("codec", &self.media_subtype_string())
419 .finish()
420 }
421}
422
423impl fmt::Display for CMFormatDescription {
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425 write!(
426 f,
427 "CMFormatDescription(type: 0x{:08X}, subtype: 0x{:08X})",
428 self.media_type_raw(),
429 self.media_subtype_raw()
430 )
431 }
432}
433
434#[derive(Clone, PartialEq, Eq, Hash)]
436pub struct CMMetadataFormatDescription(CMFormatDescription);
437
438impl CMMetadataFormatDescription {
439 #[must_use]
441 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
442 CMFormatDescription::from_raw(ptr).map(Self)
443 }
444
445 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
450 Self(CMFormatDescription::from_ptr(ptr))
451 }
452
453 #[must_use]
455 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
456 self.0.as_ptr()
457 }
458
459 #[must_use]
461 pub const fn as_format_description(&self) -> &CMFormatDescription {
462 &self.0
463 }
464
465 #[must_use]
467 pub fn into_format_description(self) -> CMFormatDescription {
468 self.0
469 }
470
471 pub fn create_with_keys(
477 metadata_type: crate::utils::four_char_code::FourCharCode,
478 keys: Option<&CFArray>,
479 ) -> Result<Self, i32> {
480 let mut ptr = std::ptr::null_mut();
481 let status = unsafe {
482 ffi::cm_metadata_format_description_create_with_keys(
483 metadata_type.into(),
484 keys.map_or(std::ptr::null_mut(), CFArray::as_ptr),
485 &mut ptr,
486 )
487 };
488 if status == 0 && !ptr.is_null() {
489 Self::from_raw(ptr).ok_or(status)
490 } else {
491 Err(status)
492 }
493 }
494
495 pub fn create_with_metadata_specifications(
501 metadata_type: crate::utils::four_char_code::FourCharCode,
502 metadata_specifications: &CFArray,
503 ) -> Result<Self, i32> {
504 let mut ptr = std::ptr::null_mut();
505 let status = unsafe {
506 ffi::cm_metadata_format_description_create_with_metadata_specifications(
507 metadata_type.into(),
508 metadata_specifications.as_ptr(),
509 &mut ptr,
510 )
511 };
512 if status == 0 && !ptr.is_null() {
513 Self::from_raw(ptr).ok_or(status)
514 } else {
515 Err(status)
516 }
517 }
518
519 pub fn extend_with_metadata_specifications(
525 &self,
526 metadata_specifications: &CFArray,
527 ) -> Result<Self, i32> {
528 let mut ptr = std::ptr::null_mut();
529 let status = unsafe {
530 ffi::cm_metadata_format_description_create_with_description_and_metadata_specifications(
531 self.as_ptr(),
532 metadata_specifications.as_ptr(),
533 &mut ptr,
534 )
535 };
536 if status == 0 && !ptr.is_null() {
537 Self::from_raw(ptr).ok_or(status)
538 } else {
539 Err(status)
540 }
541 }
542
543 pub fn merge(&self, other: &Self) -> Result<Self, i32> {
549 let mut ptr = std::ptr::null_mut();
550 let status = unsafe {
551 ffi::cm_metadata_format_description_create_by_merging_descriptions(
552 self.as_ptr(),
553 other.as_ptr(),
554 &mut ptr,
555 )
556 };
557 if status == 0 && !ptr.is_null() {
558 Self::from_raw(ptr).ok_or(status)
559 } else {
560 Err(status)
561 }
562 }
563
564 #[must_use]
566 pub fn identifiers(&self) -> Option<CFArray> {
567 let ptr = unsafe { ffi::cm_metadata_format_description_get_identifiers(self.as_ptr()) };
568 CFArray::from_raw(ptr)
569 }
570
571 #[must_use]
573 pub fn key_with_local_id(&self, local_id: u32) -> Option<CFDictionary> {
574 let ptr = unsafe {
575 ffi::cm_metadata_format_description_get_key_with_local_id(self.as_ptr(), local_id)
576 };
577 CFDictionary::from_raw(ptr)
578 }
579}
580
581impl Deref for CMMetadataFormatDescription {
582 type Target = CMFormatDescription;
583
584 fn deref(&self) -> &Self::Target {
585 &self.0
586 }
587}
588
589impl TryFrom<CMFormatDescription> for CMMetadataFormatDescription {
590 type Error = CMFormatDescription;
591
592 fn try_from(value: CMFormatDescription) -> Result<Self, Self::Error> {
593 if value.is_metadata() {
594 Ok(Self(value))
595 } else {
596 Err(value)
597 }
598 }
599}
600
601impl From<CMMetadataFormatDescription> for CMFormatDescription {
602 fn from(value: CMMetadataFormatDescription) -> Self {
603 value.0
604 }
605}
606
607impl fmt::Debug for CMMetadataFormatDescription {
608 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
609 f.debug_struct("CMMetadataFormatDescription")
610 .field("media_type", &self.media_type_string())
611 .field("codec", &self.media_subtype_string())
612 .finish()
613 }
614}
615
616impl fmt::Display for CMMetadataFormatDescription {
617 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618 fmt::Display::fmt(&self.0, f)
619 }
620}