apple_cf/cm/
format_description.rs1#![allow(dead_code)]
4
5use crate::ffi;
6use std::fmt;
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
78impl CMFormatDescription {
79 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
80 if ptr.is_null() {
81 None
82 } else {
83 Some(Self(ptr))
84 }
85 }
86
87 pub const unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
90 Self(ptr)
91 }
92
93 #[must_use]
94 pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
95 self.0
96 }
97
98 #[must_use]
100 pub fn media_type_raw(&self) -> u32 {
101 unsafe { ffi::cm_format_description_get_media_type(self.0) }
102 }
103
104 #[must_use]
106 pub fn media_type(&self) -> crate::utils::four_char_code::FourCharCode {
107 crate::utils::four_char_code::FourCharCode::from(self.media_type_raw())
108 }
109
110 #[must_use]
112 pub fn media_subtype_raw(&self) -> u32 {
113 unsafe { ffi::cm_format_description_get_media_subtype(self.0) }
114 }
115
116 #[must_use]
118 pub fn media_subtype(&self) -> crate::utils::four_char_code::FourCharCode {
119 crate::utils::four_char_code::FourCharCode::from(self.media_subtype_raw())
120 }
121
122 #[must_use]
124 pub fn extensions(&self) -> Option<*const std::ffi::c_void> {
125 unsafe {
126 let ptr = ffi::cm_format_description_get_extensions(self.0);
127 if ptr.is_null() {
128 None
129 } else {
130 Some(ptr)
131 }
132 }
133 }
134
135 #[must_use]
137 pub fn is_video(&self) -> bool {
138 self.media_type() == media_types::VIDEO
139 }
140
141 #[must_use]
143 pub fn is_audio(&self) -> bool {
144 self.media_type() == media_types::AUDIO
145 }
146
147 #[must_use]
149 pub fn is_muxed(&self) -> bool {
150 self.media_type() == media_types::MUXED
151 }
152
153 #[must_use]
155 pub fn is_text(&self) -> bool {
156 self.media_type() == media_types::TEXT
157 }
158
159 #[must_use]
161 pub fn is_closed_caption(&self) -> bool {
162 self.media_type() == media_types::CLOSED_CAPTION
163 }
164
165 #[must_use]
167 pub fn is_metadata(&self) -> bool {
168 self.media_type() == media_types::METADATA
169 }
170
171 #[must_use]
173 pub fn is_timecode(&self) -> bool {
174 self.media_type() == media_types::TIMECODE
175 }
176
177 #[must_use]
179 pub fn media_type_string(&self) -> String {
180 self.media_type().display()
181 }
182
183 #[must_use]
185 pub fn media_subtype_string(&self) -> String {
186 self.media_subtype().display()
187 }
188
189 #[must_use]
191 pub fn is_h264(&self) -> bool {
192 self.media_subtype() == codec_types::H264
193 }
194
195 #[must_use]
197 pub fn is_hevc(&self) -> bool {
198 let subtype = self.media_subtype();
199 subtype == codec_types::HEVC || subtype == codec_types::HEVC_2
200 }
201
202 #[must_use]
204 pub fn is_aac(&self) -> bool {
205 self.media_subtype() == codec_types::AAC
206 }
207
208 #[must_use]
210 pub fn is_pcm(&self) -> bool {
211 self.media_subtype() == codec_types::LPCM
212 }
213
214 #[must_use]
216 pub fn is_prores(&self) -> bool {
217 let subtype = self.media_subtype();
218 subtype == codec_types::PRORES_422 || subtype == codec_types::PRORES_4444
219 }
220
221 #[must_use]
223 pub fn is_alac(&self) -> bool {
224 self.media_subtype() == codec_types::ALAC
225 }
226
227 #[must_use]
233 pub fn audio_sample_rate(&self) -> Option<f64> {
234 if !self.is_audio() {
235 return None;
236 }
237 let rate = unsafe { ffi::cm_format_description_get_audio_sample_rate(self.0) };
238 if rate > 0.0 {
239 Some(rate)
240 } else {
241 None
242 }
243 }
244
245 #[must_use]
249 pub fn audio_channel_count(&self) -> Option<u32> {
250 if !self.is_audio() {
251 return None;
252 }
253 let count = unsafe { ffi::cm_format_description_get_audio_channel_count(self.0) };
254 if count > 0 {
255 Some(count)
256 } else {
257 None
258 }
259 }
260
261 #[must_use]
265 pub fn audio_bits_per_channel(&self) -> Option<u32> {
266 if !self.is_audio() {
267 return None;
268 }
269 let bits = unsafe { ffi::cm_format_description_get_audio_bits_per_channel(self.0) };
270 if bits > 0 {
271 Some(bits)
272 } else {
273 None
274 }
275 }
276
277 #[must_use]
281 pub fn audio_bytes_per_frame(&self) -> Option<u32> {
282 if !self.is_audio() {
283 return None;
284 }
285 let bytes = unsafe { ffi::cm_format_description_get_audio_bytes_per_frame(self.0) };
286 if bytes > 0 {
287 Some(bytes)
288 } else {
289 None
290 }
291 }
292
293 #[must_use]
297 pub fn audio_format_flags(&self) -> Option<u32> {
298 if !self.is_audio() {
299 return None;
300 }
301 Some(unsafe { ffi::cm_format_description_get_audio_format_flags(self.0) })
302 }
303
304 #[must_use]
306 pub fn audio_is_float(&self) -> bool {
307 self.audio_format_flags().is_some_and(|f| f & 1 != 0)
309 }
310
311 #[must_use]
313 pub fn audio_is_big_endian(&self) -> bool {
314 self.audio_format_flags().is_some_and(|f| f & 2 != 0)
316 }
317}
318
319impl Clone for CMFormatDescription {
320 fn clone(&self) -> Self {
321 unsafe {
322 let ptr = ffi::cm_format_description_retain(self.0);
323 Self(ptr)
324 }
325 }
326}
327
328impl Drop for CMFormatDescription {
329 fn drop(&mut self) {
330 unsafe {
331 ffi::cm_format_description_release(self.0);
332 }
333 }
334}
335
336unsafe impl Send for CMFormatDescription {}
337unsafe impl Sync for CMFormatDescription {}
338
339impl fmt::Debug for CMFormatDescription {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 f.debug_struct("CMFormatDescription")
342 .field("media_type", &self.media_type_string())
343 .field("codec", &self.media_subtype_string())
344 .finish()
345 }
346}
347
348impl fmt::Display for CMFormatDescription {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 write!(
351 f,
352 "CMFormatDescription(type: 0x{:08X}, subtype: 0x{:08X})",
353 self.media_type_raw(),
354 self.media_subtype_raw()
355 )
356 }
357}