apple_cf/cm/
format_description.rs1#![allow(dead_code)]
4
5use crate::{cf::{CFArray, CFDictionary}, ffi};
6use std::{fmt, ops::Deref};
7
8pub struct CMFormatDescription(*mut std::ffi::c_void);
9
10impl PartialEq for CMFormatDescription {
11 fn eq(&self, other: &Self) -> bool {
12 self.0 == other.0
13 }
14}
15
16impl Eq for CMFormatDescription {}
17
18impl std::hash::Hash for CMFormatDescription {
19 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
20 unsafe {
21 let hash_value = ffi::cm_format_description_hash(self.0);
22 hash_value.hash(state);
23 }
24 }
25}
26
27pub mod media_types {
29 use crate::utils::four_char_code::FourCharCode;
30
31 pub const VIDEO: FourCharCode = FourCharCode::from_bytes(*b"vide");
33 pub const AUDIO: FourCharCode = FourCharCode::from_bytes(*b"soun");
35 pub const MUXED: FourCharCode = FourCharCode::from_bytes(*b"mux ");
37 pub const TEXT: FourCharCode = FourCharCode::from_bytes(*b"text");
39 pub const CLOSED_CAPTION: FourCharCode = FourCharCode::from_bytes(*b"clcp");
41 pub const METADATA: FourCharCode = FourCharCode::from_bytes(*b"meta");
43 pub const TIMECODE: FourCharCode = FourCharCode::from_bytes(*b"tmcd");
45}
46
47pub mod codec_types {
49 use crate::utils::four_char_code::FourCharCode;
50
51 pub const H264: FourCharCode = FourCharCode::from_bytes(*b"avc1");
54 pub const HEVC: FourCharCode = FourCharCode::from_bytes(*b"hvc1");
56 pub const HEVC_2: FourCharCode = FourCharCode::from_bytes(*b"hev1");
58 pub const JPEG: FourCharCode = FourCharCode::from_bytes(*b"jpeg");
60 pub const PRORES_422: FourCharCode = FourCharCode::from_bytes(*b"apcn");
62 pub const PRORES_4444: FourCharCode = FourCharCode::from_bytes(*b"ap4h");
64
65 pub const AAC: FourCharCode = FourCharCode::from_bytes(*b"aac ");
68 pub const LPCM: FourCharCode = FourCharCode::from_bytes(*b"lpcm");
70 pub const ALAC: FourCharCode = FourCharCode::from_bytes(*b"alac");
72 pub const OPUS: FourCharCode = FourCharCode::from_bytes(*b"opus");
74 pub const FLAC: FourCharCode = FourCharCode::from_bytes(*b"flac");
76}
77
78pub mod metadata_format_types {
80 use crate::utils::four_char_code::FourCharCode;
81
82 pub const ICY: FourCharCode = FourCharCode::from_bytes(*b"icy ");
84 pub const ID3: FourCharCode = FourCharCode::from_bytes(*b"id3 ");
86 pub const BOXED: FourCharCode = FourCharCode::from_bytes(*b"mebx");
88 pub const EMSG: FourCharCode = FourCharCode::from_bytes(*b"emsg");
90}
91
92macro_rules! cfstring_constant_fn {
93 ($vis:vis fn $name:ident => $ffi_name:ident) => {
94 #[must_use]
95 $vis fn $name() -> CFString {
96 let ptr = unsafe { ffi::$ffi_name() };
97 CFString::from_raw(ptr).expect(concat!(stringify!($ffi_name), " returned NULL"))
98 }
99 };
100}
101
102pub mod format_description_extension_keys {
104 use crate::{cf::CFString, ffi};
105
106 cfstring_constant_fn!(pub fn metadata_key_table => cm_metadata_format_description_extension_key_metadata_key_table);
107}
108
109pub mod metadata_description_keys {
111 use crate::{cf::CFString, ffi};
112
113 cfstring_constant_fn!(pub fn conforming_data_types => cm_metadata_format_description_key_conforming_data_types);
114 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_key_data_type);
115 cfstring_constant_fn!(pub fn data_type_namespace => cm_metadata_format_description_key_data_type_namespace);
116 cfstring_constant_fn!(pub fn language_tag => cm_metadata_format_description_key_language_tag);
117 cfstring_constant_fn!(pub fn local_id => cm_metadata_format_description_key_local_id);
118 cfstring_constant_fn!(pub fn namespace => cm_metadata_format_description_key_namespace);
119 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_key_setup_data);
120 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_key_structural_dependency);
121 cfstring_constant_fn!(pub fn value => cm_metadata_format_description_key_value);
122}
123
124pub mod metadata_specification_keys {
126 use crate::{cf::CFString, ffi};
127
128 cfstring_constant_fn!(pub fn data_type => cm_metadata_format_description_metadata_specification_key_data_type);
129 cfstring_constant_fn!(pub fn extended_language_tag => cm_metadata_format_description_metadata_specification_key_extended_language_tag);
130 cfstring_constant_fn!(pub fn identifier => cm_metadata_format_description_metadata_specification_key_identifier);
131 cfstring_constant_fn!(pub fn setup_data => cm_metadata_format_description_metadata_specification_key_setup_data);
132 cfstring_constant_fn!(pub fn structural_dependency => cm_metadata_format_description_metadata_specification_key_structural_dependency);
133}
134
135pub mod metadata_structural_dependency_keys {
137 use crate::{cf::CFString, ffi};
138
139 cfstring_constant_fn!(pub fn dependency_is_invalid_flag => cm_metadata_format_description_structural_dependency_key_dependency_is_invalid_flag);
140}
141
142impl CMFormatDescription {
143 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
144 if ptr.is_null() {
145 None
146 } else {
147 Some(Self(ptr))
148 }
149 }
150
151 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
154 Self(ptr)
155 }
156
157 #[must_use]
158 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
159 self.0
160 }
161
162 #[must_use]
164 pub fn media_type_raw(&self) -> u32 {
165 unsafe { ffi::cm_format_description_get_media_type(self.0) }
166 }
167
168 #[must_use]
170 pub fn media_type(&self) -> crate::utils::four_char_code::FourCharCode {
171 crate::utils::four_char_code::FourCharCode::from(self.media_type_raw())
172 }
173
174 #[must_use]
176 pub fn media_subtype_raw(&self) -> u32 {
177 unsafe { ffi::cm_format_description_get_media_subtype(self.0) }
178 }
179
180 #[must_use]
182 pub fn media_subtype(&self) -> crate::utils::four_char_code::FourCharCode {
183 crate::utils::four_char_code::FourCharCode::from(self.media_subtype_raw())
184 }
185
186 #[must_use]
188 pub fn extensions(&self) -> Option<*const std::ffi::c_void> {
189 unsafe {
190 let ptr = ffi::cm_format_description_get_extensions(self.0);
191 if ptr.is_null() {
192 None
193 } else {
194 Some(ptr)
195 }
196 }
197 }
198
199 #[must_use]
201 pub fn is_video(&self) -> bool {
202 self.media_type() == media_types::VIDEO
203 }
204
205 #[must_use]
207 pub fn is_audio(&self) -> bool {
208 self.media_type() == media_types::AUDIO
209 }
210
211 #[must_use]
213 pub fn is_muxed(&self) -> bool {
214 self.media_type() == media_types::MUXED
215 }
216
217 #[must_use]
219 pub fn is_text(&self) -> bool {
220 self.media_type() == media_types::TEXT
221 }
222
223 #[must_use]
225 pub fn is_closed_caption(&self) -> bool {
226 self.media_type() == media_types::CLOSED_CAPTION
227 }
228
229 #[must_use]
231 pub fn is_metadata(&self) -> bool {
232 self.media_type() == media_types::METADATA
233 }
234
235 #[must_use]
237 pub fn is_timecode(&self) -> bool {
238 self.media_type() == media_types::TIMECODE
239 }
240
241 #[must_use]
243 pub fn media_type_string(&self) -> String {
244 self.media_type().display()
245 }
246
247 #[must_use]
249 pub fn media_subtype_string(&self) -> String {
250 self.media_subtype().display()
251 }
252
253 #[must_use]
255 pub fn is_h264(&self) -> bool {
256 self.media_subtype() == codec_types::H264
257 }
258
259 #[must_use]
261 pub fn is_hevc(&self) -> bool {
262 let subtype = self.media_subtype();
263 subtype == codec_types::HEVC || subtype == codec_types::HEVC_2
264 }
265
266 #[must_use]
268 pub fn is_aac(&self) -> bool {
269 self.media_subtype() == codec_types::AAC
270 }
271
272 #[must_use]
274 pub fn is_pcm(&self) -> bool {
275 self.media_subtype() == codec_types::LPCM
276 }
277
278 #[must_use]
280 pub fn is_prores(&self) -> bool {
281 let subtype = self.media_subtype();
282 subtype == codec_types::PRORES_422 || subtype == codec_types::PRORES_4444
283 }
284
285 #[must_use]
287 pub fn is_alac(&self) -> bool {
288 self.media_subtype() == codec_types::ALAC
289 }
290
291 #[must_use]
297 pub fn audio_sample_rate(&self) -> Option<f64> {
298 if !self.is_audio() {
299 return None;
300 }
301 let rate = unsafe { ffi::cm_format_description_get_audio_sample_rate(self.0) };
302 if rate > 0.0 {
303 Some(rate)
304 } else {
305 None
306 }
307 }
308
309 #[must_use]
313 pub fn audio_channel_count(&self) -> Option<u32> {
314 if !self.is_audio() {
315 return None;
316 }
317 let count = unsafe { ffi::cm_format_description_get_audio_channel_count(self.0) };
318 if count > 0 {
319 Some(count)
320 } else {
321 None
322 }
323 }
324
325 #[must_use]
329 pub fn audio_bits_per_channel(&self) -> Option<u32> {
330 if !self.is_audio() {
331 return None;
332 }
333 let bits = unsafe { ffi::cm_format_description_get_audio_bits_per_channel(self.0) };
334 if bits > 0 {
335 Some(bits)
336 } else {
337 None
338 }
339 }
340
341 #[must_use]
345 pub fn audio_bytes_per_frame(&self) -> Option<u32> {
346 if !self.is_audio() {
347 return None;
348 }
349 let bytes = unsafe { ffi::cm_format_description_get_audio_bytes_per_frame(self.0) };
350 if bytes > 0 {
351 Some(bytes)
352 } else {
353 None
354 }
355 }
356
357 #[must_use]
361 pub fn audio_format_flags(&self) -> Option<u32> {
362 if !self.is_audio() {
363 return None;
364 }
365 Some(unsafe { ffi::cm_format_description_get_audio_format_flags(self.0) })
366 }
367
368 #[must_use]
370 pub fn audio_is_float(&self) -> bool {
371 self.audio_format_flags().is_some_and(|f| f & 1 != 0)
373 }
374
375 #[must_use]
377 pub fn audio_is_big_endian(&self) -> bool {
378 self.audio_format_flags().is_some_and(|f| f & 2 != 0)
380 }
381}
382
383impl Clone for CMFormatDescription {
384 fn clone(&self) -> Self {
385 unsafe {
386 let ptr = ffi::cm_format_description_retain(self.0);
387 Self(ptr)
388 }
389 }
390}
391
392impl Drop for CMFormatDescription {
393 fn drop(&mut self) {
394 unsafe {
395 ffi::cm_format_description_release(self.0);
396 }
397 }
398}
399
400unsafe impl Send for CMFormatDescription {}
401unsafe impl Sync for CMFormatDescription {}
402
403impl fmt::Debug for CMFormatDescription {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 f.debug_struct("CMFormatDescription")
406 .field("media_type", &self.media_type_string())
407 .field("codec", &self.media_subtype_string())
408 .finish()
409 }
410}
411
412impl fmt::Display for CMFormatDescription {
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 write!(
415 f,
416 "CMFormatDescription(type: 0x{:08X}, subtype: 0x{:08X})",
417 self.media_type_raw(),
418 self.media_subtype_raw()
419 )
420 }
421}
422
423#[derive(Clone, PartialEq, Eq, Hash)]
425pub struct CMMetadataFormatDescription(CMFormatDescription);
426
427impl CMMetadataFormatDescription {
428 #[must_use]
430 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
431 CMFormatDescription::from_raw(ptr).map(Self)
432 }
433
434 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
437 Self(CMFormatDescription::from_ptr(ptr))
438 }
439
440 #[must_use]
441 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
442 self.0.as_ptr()
443 }
444
445 #[must_use]
447 pub const fn as_format_description(&self) -> &CMFormatDescription {
448 &self.0
449 }
450
451 #[must_use]
453 pub fn into_format_description(self) -> CMFormatDescription {
454 self.0
455 }
456
457 pub fn create_with_keys(
463 metadata_type: crate::utils::four_char_code::FourCharCode,
464 keys: Option<&CFArray>,
465 ) -> Result<Self, i32> {
466 let mut ptr = std::ptr::null_mut();
467 let status = unsafe {
468 ffi::cm_metadata_format_description_create_with_keys(
469 metadata_type.into(),
470 keys.map_or(std::ptr::null_mut(), CFArray::as_ptr),
471 &mut ptr,
472 )
473 };
474 if status == 0 && !ptr.is_null() {
475 Self::from_raw(ptr).ok_or(status)
476 } else {
477 Err(status)
478 }
479 }
480
481 pub fn create_with_metadata_specifications(
487 metadata_type: crate::utils::four_char_code::FourCharCode,
488 metadata_specifications: &CFArray,
489 ) -> Result<Self, i32> {
490 let mut ptr = std::ptr::null_mut();
491 let status = unsafe {
492 ffi::cm_metadata_format_description_create_with_metadata_specifications(
493 metadata_type.into(),
494 metadata_specifications.as_ptr(),
495 &mut ptr,
496 )
497 };
498 if status == 0 && !ptr.is_null() {
499 Self::from_raw(ptr).ok_or(status)
500 } else {
501 Err(status)
502 }
503 }
504
505 pub fn extend_with_metadata_specifications(
511 &self,
512 metadata_specifications: &CFArray,
513 ) -> Result<Self, i32> {
514 let mut ptr = std::ptr::null_mut();
515 let status = unsafe {
516 ffi::cm_metadata_format_description_create_with_description_and_metadata_specifications(
517 self.as_ptr(),
518 metadata_specifications.as_ptr(),
519 &mut ptr,
520 )
521 };
522 if status == 0 && !ptr.is_null() {
523 Self::from_raw(ptr).ok_or(status)
524 } else {
525 Err(status)
526 }
527 }
528
529 pub fn merge(&self, other: &Self) -> Result<Self, i32> {
535 let mut ptr = std::ptr::null_mut();
536 let status = unsafe {
537 ffi::cm_metadata_format_description_create_by_merging_descriptions(
538 self.as_ptr(),
539 other.as_ptr(),
540 &mut ptr,
541 )
542 };
543 if status == 0 && !ptr.is_null() {
544 Self::from_raw(ptr).ok_or(status)
545 } else {
546 Err(status)
547 }
548 }
549
550 #[must_use]
552 pub fn identifiers(&self) -> Option<CFArray> {
553 let ptr = unsafe { ffi::cm_metadata_format_description_get_identifiers(self.as_ptr()) };
554 CFArray::from_raw(ptr)
555 }
556
557 #[must_use]
559 pub fn key_with_local_id(&self, local_id: u32) -> Option<CFDictionary> {
560 let ptr = unsafe {
561 ffi::cm_metadata_format_description_get_key_with_local_id(self.as_ptr(), local_id)
562 };
563 CFDictionary::from_raw(ptr)
564 }
565}
566
567impl Deref for CMMetadataFormatDescription {
568 type Target = CMFormatDescription;
569
570 fn deref(&self) -> &Self::Target {
571 &self.0
572 }
573}
574
575impl TryFrom<CMFormatDescription> for CMMetadataFormatDescription {
576 type Error = CMFormatDescription;
577
578 fn try_from(value: CMFormatDescription) -> Result<Self, Self::Error> {
579 if value.is_metadata() {
580 Ok(Self(value))
581 } else {
582 Err(value)
583 }
584 }
585}
586
587impl From<CMMetadataFormatDescription> for CMFormatDescription {
588 fn from(value: CMMetadataFormatDescription) -> Self {
589 value.0
590 }
591}
592
593impl fmt::Debug for CMMetadataFormatDescription {
594 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
595 f.debug_struct("CMMetadataFormatDescription")
596 .field("media_type", &self.media_type_string())
597 .field("codec", &self.media_subtype_string())
598 .finish()
599 }
600}
601
602impl fmt::Display for CMMetadataFormatDescription {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 fmt::Display::fmt(&self.0, f)
605 }
606}