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);
12
13impl PartialEq for CMFormatDescription {
14 fn eq(&self, other: &Self) -> bool {
15 self.0 == other.0
16 }
17}
18
19impl Eq for CMFormatDescription {}
20
21impl std::hash::Hash for CMFormatDescription {
22 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
23 unsafe {
24 let hash_value = ffi::cm_format_description_hash(self.0);
25 hash_value.hash(state);
26 }
27 }
28}
29
30pub mod media_types {
32 use crate::utils::four_char_code::FourCharCode;
33
34 pub const VIDEO: FourCharCode = FourCharCode::from_bytes(*b"vide");
36 pub const AUDIO: FourCharCode = FourCharCode::from_bytes(*b"soun");
38 pub const MUXED: FourCharCode = FourCharCode::from_bytes(*b"mux ");
40 pub const TEXT: FourCharCode = FourCharCode::from_bytes(*b"text");
42 pub const CLOSED_CAPTION: FourCharCode = FourCharCode::from_bytes(*b"clcp");
44 pub const METADATA: FourCharCode = FourCharCode::from_bytes(*b"meta");
46 pub const TIMECODE: FourCharCode = FourCharCode::from_bytes(*b"tmcd");
48}
49
50pub mod codec_types {
52 use crate::utils::four_char_code::FourCharCode;
53
54 pub const H264: FourCharCode = FourCharCode::from_bytes(*b"avc1");
57 pub const HEVC: FourCharCode = FourCharCode::from_bytes(*b"hvc1");
59 pub const HEVC_2: FourCharCode = FourCharCode::from_bytes(*b"hev1");
61 pub const JPEG: FourCharCode = FourCharCode::from_bytes(*b"jpeg");
63 pub const PRORES_422: FourCharCode = FourCharCode::from_bytes(*b"apcn");
65 pub const PRORES_4444: FourCharCode = FourCharCode::from_bytes(*b"ap4h");
67
68 pub const AAC: FourCharCode = FourCharCode::from_bytes(*b"aac ");
71 pub const LPCM: FourCharCode = FourCharCode::from_bytes(*b"lpcm");
73 pub const ALAC: FourCharCode = FourCharCode::from_bytes(*b"alac");
75 pub const OPUS: FourCharCode = FourCharCode::from_bytes(*b"opus");
77 pub const FLAC: FourCharCode = FourCharCode::from_bytes(*b"flac");
79}
80
81pub mod metadata_format_types {
83 use crate::utils::four_char_code::FourCharCode;
84
85 pub const ICY: FourCharCode = FourCharCode::from_bytes(*b"icy ");
87 pub const ID3: FourCharCode = FourCharCode::from_bytes(*b"id3 ");
89 pub const BOXED: FourCharCode = FourCharCode::from_bytes(*b"mebx");
91 pub const EMSG: FourCharCode = FourCharCode::from_bytes(*b"emsg");
93}
94
95macro_rules! cfstring_constant_fn {
96 ($vis:vis fn $name:ident => $ffi_name:ident) => {
97 #[must_use]
98 $vis fn $name() -> CFString {
99 let ptr = unsafe { ffi::$ffi_name() };
100 CFString::from_raw(ptr).expect(concat!(stringify!($ffi_name), " returned NULL"))
101 }
102 };
103}
104
105pub mod format_description_extension_keys {
107 use crate::{cf::CFString, ffi};
108
109 cfstring_constant_fn!(pub fn metadata_key_table => cm_metadata_format_description_extension_key_metadata_key_table);
110}
111
112pub mod metadata_description_keys {
114 use crate::{cf::CFString, ffi};
115
116 cfstring_constant_fn!(pub fn conforming_data_types => cm_metadata_format_description_key_conforming_data_types);
117 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_key_data_type);
118 cfstring_constant_fn!(pub fn data_type_namespace => cm_metadata_format_description_key_data_type_namespace);
119 cfstring_constant_fn!(pub fn language_tag => cm_metadata_format_description_key_language_tag);
120 cfstring_constant_fn!(pub fn local_id => cm_metadata_format_description_key_local_id);
121 cfstring_constant_fn!(pub fn namespace => cm_metadata_format_description_key_namespace);
122 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_key_setup_data);
123 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_key_structural_dependency);
124 cfstring_constant_fn!(pub fn value => cm_metadata_format_description_key_value);
125}
126
127pub mod metadata_specification_keys {
129 use crate::{cf::CFString, ffi};
130
131 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_metadata_specification_key_data_type);
132 cfstring_constant_fn!(pub fn extended_language_tag => cm_metadata_format_description_metadata_specification_key_extended_language_tag);
133 cfstring_constant_fn!(pub fn identifier => cm_metadata_format_description_metadata_specification_key_identifier);
134 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_metadata_specification_key_setup_data);
135 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_metadata_specification_key_structural_dependency);
136}
137
138pub mod metadata_structural_dependency_keys {
140 use crate::{cf::CFString, ffi};
141
142 cfstring_constant_fn!(pub fn dependency_is_invalid_flag => cm_metadata_format_description_structural_dependency_key_dependency_is_invalid_flag);
143}
144
145impl CMFormatDescription {
146 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
147 if ptr.is_null() {
148 None
149 } else {
150 Some(Self(ptr))
151 }
152 }
153
154 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
157 Self(ptr)
158 }
159
160 #[must_use]
161 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
162 self.0
163 }
164
165 #[must_use]
167 pub fn media_type_raw(&self) -> u32 {
168 unsafe { ffi::cm_format_description_get_media_type(self.0) }
169 }
170
171 #[must_use]
173 pub fn media_type(&self) -> crate::utils::four_char_code::FourCharCode {
174 crate::utils::four_char_code::FourCharCode::from(self.media_type_raw())
175 }
176
177 #[must_use]
179 pub fn media_subtype_raw(&self) -> u32 {
180 unsafe { ffi::cm_format_description_get_media_subtype(self.0) }
181 }
182
183 #[must_use]
185 pub fn media_subtype(&self) -> crate::utils::four_char_code::FourCharCode {
186 crate::utils::four_char_code::FourCharCode::from(self.media_subtype_raw())
187 }
188
189 #[must_use]
191 pub fn extensions(&self) -> Option<*const std::ffi::c_void> {
192 unsafe {
193 let ptr = ffi::cm_format_description_get_extensions(self.0);
194 if ptr.is_null() {
195 None
196 } else {
197 Some(ptr)
198 }
199 }
200 }
201
202 #[must_use]
204 pub fn is_video(&self) -> bool {
205 self.media_type() == media_types::VIDEO
206 }
207
208 #[must_use]
210 pub fn is_audio(&self) -> bool {
211 self.media_type() == media_types::AUDIO
212 }
213
214 #[must_use]
216 pub fn is_muxed(&self) -> bool {
217 self.media_type() == media_types::MUXED
218 }
219
220 #[must_use]
222 pub fn is_text(&self) -> bool {
223 self.media_type() == media_types::TEXT
224 }
225
226 #[must_use]
228 pub fn is_closed_caption(&self) -> bool {
229 self.media_type() == media_types::CLOSED_CAPTION
230 }
231
232 #[must_use]
234 pub fn is_metadata(&self) -> bool {
235 self.media_type() == media_types::METADATA
236 }
237
238 #[must_use]
240 pub fn is_timecode(&self) -> bool {
241 self.media_type() == media_types::TIMECODE
242 }
243
244 #[must_use]
246 pub fn media_type_string(&self) -> String {
247 self.media_type().display()
248 }
249
250 #[must_use]
252 pub fn media_subtype_string(&self) -> String {
253 self.media_subtype().display()
254 }
255
256 #[must_use]
258 pub fn is_h264(&self) -> bool {
259 self.media_subtype() == codec_types::H264
260 }
261
262 #[must_use]
264 pub fn is_hevc(&self) -> bool {
265 let subtype = self.media_subtype();
266 subtype == codec_types::HEVC || subtype == codec_types::HEVC_2
267 }
268
269 #[must_use]
271 pub fn is_aac(&self) -> bool {
272 self.media_subtype() == codec_types::AAC
273 }
274
275 #[must_use]
277 pub fn is_pcm(&self) -> bool {
278 self.media_subtype() == codec_types::LPCM
279 }
280
281 #[must_use]
283 pub fn is_prores(&self) -> bool {
284 let subtype = self.media_subtype();
285 subtype == codec_types::PRORES_422 || subtype == codec_types::PRORES_4444
286 }
287
288 #[must_use]
290 pub fn is_alac(&self) -> bool {
291 self.media_subtype() == codec_types::ALAC
292 }
293
294 #[must_use]
300 pub fn audio_sample_rate(&self) -> Option<f64> {
301 if !self.is_audio() {
302 return None;
303 }
304 let rate = unsafe { ffi::cm_format_description_get_audio_sample_rate(self.0) };
305 if rate > 0.0 {
306 Some(rate)
307 } else {
308 None
309 }
310 }
311
312 #[must_use]
316 pub fn audio_channel_count(&self) -> Option<u32> {
317 if !self.is_audio() {
318 return None;
319 }
320 let count = unsafe { ffi::cm_format_description_get_audio_channel_count(self.0) };
321 if count > 0 {
322 Some(count)
323 } else {
324 None
325 }
326 }
327
328 #[must_use]
332 pub fn audio_bits_per_channel(&self) -> Option<u32> {
333 if !self.is_audio() {
334 return None;
335 }
336 let bits = unsafe { ffi::cm_format_description_get_audio_bits_per_channel(self.0) };
337 if bits > 0 {
338 Some(bits)
339 } else {
340 None
341 }
342 }
343
344 #[must_use]
348 pub fn audio_bytes_per_frame(&self) -> Option<u32> {
349 if !self.is_audio() {
350 return None;
351 }
352 let bytes = unsafe { ffi::cm_format_description_get_audio_bytes_per_frame(self.0) };
353 if bytes > 0 {
354 Some(bytes)
355 } else {
356 None
357 }
358 }
359
360 #[must_use]
364 pub fn audio_format_flags(&self) -> Option<u32> {
365 if !self.is_audio() {
366 return None;
367 }
368 Some(unsafe { ffi::cm_format_description_get_audio_format_flags(self.0) })
369 }
370
371 #[must_use]
373 pub fn audio_is_float(&self) -> bool {
374 self.audio_format_flags().is_some_and(|f| f & 1 != 0)
376 }
377
378 #[must_use]
380 pub fn audio_is_big_endian(&self) -> bool {
381 self.audio_format_flags().is_some_and(|f| f & 2 != 0)
383 }
384}
385
386impl Clone for CMFormatDescription {
387 fn clone(&self) -> Self {
388 unsafe {
389 let ptr = ffi::cm_format_description_retain(self.0);
390 Self(ptr)
391 }
392 }
393}
394
395impl Drop for CMFormatDescription {
396 fn drop(&mut self) {
397 unsafe {
398 ffi::cm_format_description_release(self.0);
399 }
400 }
401}
402
403unsafe impl Send for CMFormatDescription {}
404unsafe impl Sync for CMFormatDescription {}
405
406impl fmt::Debug for CMFormatDescription {
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 f.debug_struct("CMFormatDescription")
409 .field("media_type", &self.media_type_string())
410 .field("codec", &self.media_subtype_string())
411 .finish()
412 }
413}
414
415impl fmt::Display for CMFormatDescription {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 write!(
418 f,
419 "CMFormatDescription(type: 0x{:08X}, subtype: 0x{:08X})",
420 self.media_type_raw(),
421 self.media_subtype_raw()
422 )
423 }
424}
425
426#[derive(Clone, PartialEq, Eq, Hash)]
428pub struct CMMetadataFormatDescription(CMFormatDescription);
429
430impl CMMetadataFormatDescription {
431 #[must_use]
433 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
434 CMFormatDescription::from_raw(ptr).map(Self)
435 }
436
437 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
440 Self(CMFormatDescription::from_ptr(ptr))
441 }
442
443 #[must_use]
444 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
445 self.0.as_ptr()
446 }
447
448 #[must_use]
450 pub const fn as_format_description(&self) -> &CMFormatDescription {
451 &self.0
452 }
453
454 #[must_use]
456 pub fn into_format_description(self) -> CMFormatDescription {
457 self.0
458 }
459
460 pub fn create_with_keys(
466 metadata_type: crate::utils::four_char_code::FourCharCode,
467 keys: Option<&CFArray>,
468 ) -> Result<Self, i32> {
469 let mut ptr = std::ptr::null_mut();
470 let status = unsafe {
471 ffi::cm_metadata_format_description_create_with_keys(
472 metadata_type.into(),
473 keys.map_or(std::ptr::null_mut(), CFArray::as_ptr),
474 &mut ptr,
475 )
476 };
477 if status == 0 && !ptr.is_null() {
478 Self::from_raw(ptr).ok_or(status)
479 } else {
480 Err(status)
481 }
482 }
483
484 pub fn create_with_metadata_specifications(
490 metadata_type: crate::utils::four_char_code::FourCharCode,
491 metadata_specifications: &CFArray,
492 ) -> Result<Self, i32> {
493 let mut ptr = std::ptr::null_mut();
494 let status = unsafe {
495 ffi::cm_metadata_format_description_create_with_metadata_specifications(
496 metadata_type.into(),
497 metadata_specifications.as_ptr(),
498 &mut ptr,
499 )
500 };
501 if status == 0 && !ptr.is_null() {
502 Self::from_raw(ptr).ok_or(status)
503 } else {
504 Err(status)
505 }
506 }
507
508 pub fn extend_with_metadata_specifications(
514 &self,
515 metadata_specifications: &CFArray,
516 ) -> Result<Self, i32> {
517 let mut ptr = std::ptr::null_mut();
518 let status = unsafe {
519 ffi::cm_metadata_format_description_create_with_description_and_metadata_specifications(
520 self.as_ptr(),
521 metadata_specifications.as_ptr(),
522 &mut ptr,
523 )
524 };
525 if status == 0 && !ptr.is_null() {
526 Self::from_raw(ptr).ok_or(status)
527 } else {
528 Err(status)
529 }
530 }
531
532 pub fn merge(&self, other: &Self) -> Result<Self, i32> {
538 let mut ptr = std::ptr::null_mut();
539 let status = unsafe {
540 ffi::cm_metadata_format_description_create_by_merging_descriptions(
541 self.as_ptr(),
542 other.as_ptr(),
543 &mut ptr,
544 )
545 };
546 if status == 0 && !ptr.is_null() {
547 Self::from_raw(ptr).ok_or(status)
548 } else {
549 Err(status)
550 }
551 }
552
553 #[must_use]
555 pub fn identifiers(&self) -> Option<CFArray> {
556 let ptr = unsafe { ffi::cm_metadata_format_description_get_identifiers(self.as_ptr()) };
557 CFArray::from_raw(ptr)
558 }
559
560 #[must_use]
562 pub fn key_with_local_id(&self, local_id: u32) -> Option<CFDictionary> {
563 let ptr = unsafe {
564 ffi::cm_metadata_format_description_get_key_with_local_id(self.as_ptr(), local_id)
565 };
566 CFDictionary::from_raw(ptr)
567 }
568}
569
570impl Deref for CMMetadataFormatDescription {
571 type Target = CMFormatDescription;
572
573 fn deref(&self) -> &Self::Target {
574 &self.0
575 }
576}
577
578impl TryFrom<CMFormatDescription> for CMMetadataFormatDescription {
579 type Error = CMFormatDescription;
580
581 fn try_from(value: CMFormatDescription) -> Result<Self, Self::Error> {
582 if value.is_metadata() {
583 Ok(Self(value))
584 } else {
585 Err(value)
586 }
587 }
588}
589
590impl From<CMMetadataFormatDescription> for CMFormatDescription {
591 fn from(value: CMMetadataFormatDescription) -> Self {
592 value.0
593 }
594}
595
596impl fmt::Debug for CMMetadataFormatDescription {
597 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
598 f.debug_struct("CMMetadataFormatDescription")
599 .field("media_type", &self.media_type_string())
600 .field("codec", &self.media_subtype_string())
601 .finish()
602 }
603}
604
605impl fmt::Display for CMMetadataFormatDescription {
606 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607 fmt::Display::fmt(&self.0, f)
608 }
609}