1#![allow(clippy::missing_panics_doc)]
2
3use std::ffi::c_void;
6use std::fmt;
7
8#[repr(C)]
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct CMTime {
30 pub value: i64,
31 pub timescale: i32,
32 pub flags: u32,
33 pub epoch: i64,
34}
35
36impl std::hash::Hash for CMTime {
37 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
38 self.value.hash(state);
39 self.timescale.hash(state);
40 self.flags.hash(state);
41 self.epoch.hash(state);
42 }
43}
44
45#[repr(C)]
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub struct CMSampleTimingInfo {
66 pub duration: CMTime,
67 pub presentation_time_stamp: CMTime,
68 pub decode_time_stamp: CMTime,
69}
70
71impl std::hash::Hash for CMSampleTimingInfo {
72 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
73 self.duration.hash(state);
74 self.presentation_time_stamp.hash(state);
75 self.decode_time_stamp.hash(state);
76 }
77}
78
79impl CMSampleTimingInfo {
80 #[must_use]
91 pub const fn new() -> Self {
92 Self {
93 duration: CMTime::INVALID,
94 presentation_time_stamp: CMTime::INVALID,
95 decode_time_stamp: CMTime::INVALID,
96 }
97 }
98
99 #[must_use]
101 pub const fn with_times(
102 duration: CMTime,
103 presentation_time_stamp: CMTime,
104 decode_time_stamp: CMTime,
105 ) -> Self {
106 Self {
107 duration,
108 presentation_time_stamp,
109 decode_time_stamp,
110 }
111 }
112
113 #[must_use]
116 pub const fn is_valid(&self) -> bool {
117 self.duration.is_valid()
118 && self.presentation_time_stamp.is_valid()
119 && self.decode_time_stamp.is_valid()
120 }
121
122 #[must_use]
124 pub const fn has_valid_presentation_time(&self) -> bool {
125 self.presentation_time_stamp.is_valid()
126 }
127
128 #[must_use]
130 pub const fn has_valid_decode_time(&self) -> bool {
131 self.decode_time_stamp.is_valid()
132 }
133
134 #[must_use]
136 pub const fn has_valid_duration(&self) -> bool {
137 self.duration.is_valid()
138 }
139
140 #[must_use]
142 pub fn presentation_seconds(&self) -> Option<f64> {
143 self.presentation_time_stamp.as_seconds()
144 }
145
146 #[must_use]
148 pub fn decode_seconds(&self) -> Option<f64> {
149 self.decode_time_stamp.as_seconds()
150 }
151
152 #[must_use]
154 pub fn duration_seconds(&self) -> Option<f64> {
155 self.duration.as_seconds()
156 }
157}
158
159impl Default for CMSampleTimingInfo {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165impl fmt::Display for CMSampleTimingInfo {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 write!(
168 f,
169 "CMSampleTimingInfo(pts: {}, dts: {}, duration: {})",
170 self.presentation_time_stamp, self.decode_time_stamp, self.duration
171 )
172 }
173}
174
175impl CMTime {
176 const FLAG_VALID: u32 = 1 << 0;
177 const FLAG_HAS_BEEN_ROUNDED: u32 = 1 << 1;
178 const FLAG_POSITIVE_INFINITY: u32 = 1 << 2;
179 const FLAG_NEGATIVE_INFINITY: u32 = 1 << 3;
180 const FLAG_INDEFINITE: u32 = 1 << 4;
181 const FLAG_IMPLIED_VALUE_MASK: u32 =
182 Self::FLAG_POSITIVE_INFINITY | Self::FLAG_NEGATIVE_INFINITY | Self::FLAG_INDEFINITE;
183
184 pub const ZERO: Self = Self {
186 value: 0,
187 timescale: 1,
188 flags: Self::FLAG_VALID,
189 epoch: 0,
190 };
191
192 pub const INVALID: Self = Self {
194 value: 0,
195 timescale: 0,
196 flags: 0,
197 epoch: 0,
198 };
199
200 #[must_use]
202 pub const fn new(value: i64, timescale: i32) -> Self {
203 if timescale > 0 {
204 Self {
205 value,
206 timescale,
207 flags: Self::FLAG_VALID,
208 epoch: 0,
209 }
210 } else {
211 Self::INVALID
212 }
213 }
214
215 #[must_use]
217 pub const fn is_valid(&self) -> bool {
218 self.flags & Self::FLAG_VALID != 0
219 }
220
221 #[must_use]
223 pub const fn is_numeric(&self) -> bool {
224 self.flags & (Self::FLAG_VALID | Self::FLAG_IMPLIED_VALUE_MASK) == Self::FLAG_VALID
225 }
226
227 #[must_use]
229 pub fn is_zero(&self) -> bool {
230 self.compare(Self::ZERO).is_eq()
231 }
232
233 #[must_use]
235 pub const fn is_indefinite(&self) -> bool {
236 self.is_valid() && self.flags & Self::FLAG_INDEFINITE != 0
237 }
238
239 #[must_use]
241 pub const fn is_positive_infinity(&self) -> bool {
242 self.is_valid() && self.flags & Self::FLAG_POSITIVE_INFINITY != 0
243 }
244
245 #[must_use]
247 pub const fn is_negative_infinity(&self) -> bool {
248 self.is_valid() && self.flags & Self::FLAG_NEGATIVE_INFINITY != 0
249 }
250
251 #[must_use]
253 pub const fn has_been_rounded(&self) -> bool {
254 self.is_numeric() && self.flags & Self::FLAG_HAS_BEEN_ROUNDED != 0
255 }
256
257 #[must_use]
259 pub fn equals(&self, other: &Self) -> bool {
260 self.compare(*other).is_eq()
261 }
262
263 #[must_use]
265 pub const fn positive_infinity() -> Self {
266 Self {
267 value: 0,
268 timescale: 0,
269 flags: Self::FLAG_VALID | Self::FLAG_POSITIVE_INFINITY,
270 epoch: 0,
271 }
272 }
273
274 #[must_use]
276 pub const fn negative_infinity() -> Self {
277 Self {
278 value: 0,
279 timescale: 0,
280 flags: Self::FLAG_VALID | Self::FLAG_NEGATIVE_INFINITY,
281 epoch: 0,
282 }
283 }
284
285 #[must_use]
287 pub const fn indefinite() -> Self {
288 Self {
289 value: 0,
290 timescale: 0,
291 flags: Self::FLAG_VALID | Self::FLAG_INDEFINITE,
292 epoch: 0,
293 }
294 }
295
296 #[must_use]
298 pub fn as_seconds(&self) -> Option<f64> {
299 if self.is_numeric() && self.timescale > 0 {
300 #[allow(clippy::cast_precision_loss)]
302 Some(self.value as f64 / f64::from(self.timescale))
303 } else {
304 None
305 }
306 }
307
308 #[must_use]
312 pub fn from_seconds(seconds: f64, preferred_timescale: i32) -> Self {
313 extern "C" {
314 fn CMTimeMakeWithSeconds(seconds: f64, preferredTimescale: i32) -> CMTime;
315 }
316 unsafe { CMTimeMakeWithSeconds(seconds, preferred_timescale) }
317 }
318
319 #[must_use]
322 #[allow(clippy::should_implement_trait)]
323 pub fn add(self, other: Self) -> Self {
324 extern "C" {
325 fn CMTimeAdd(addend1: CMTime, addend2: CMTime) -> CMTime;
326 }
327 unsafe { CMTimeAdd(self, other) }
328 }
329
330 #[must_use]
332 #[allow(clippy::should_implement_trait)]
333 pub fn subtract(self, other: Self) -> Self {
334 extern "C" {
335 fn CMTimeSubtract(minuend: CMTime, subtrahend: CMTime) -> CMTime;
336 }
337 unsafe { CMTimeSubtract(self, other) }
338 }
339
340 #[must_use]
342 pub fn multiply(self, multiplier: i32) -> Self {
343 extern "C" {
344 fn CMTimeMultiply(time: CMTime, multiplier: i32) -> CMTime;
345 }
346 unsafe { CMTimeMultiply(self, multiplier) }
347 }
348
349 #[must_use]
351 pub fn multiply_by_f64(self, factor: f64) -> Self {
352 extern "C" {
353 fn CMTimeMultiplyByFloat64(time: CMTime, multiplier: f64) -> CMTime;
354 }
355 unsafe { CMTimeMultiplyByFloat64(self, factor) }
356 }
357
358 #[must_use]
362 pub fn compare(self, other: Self) -> core::cmp::Ordering {
363 extern "C" {
364 fn CMTimeCompare(time1: CMTime, time2: CMTime) -> i32;
365 }
366 let c = unsafe { CMTimeCompare(self, other) };
367 c.cmp(&0)
368 }
369
370 #[must_use]
374 pub fn convert_scale(self, new_timescale: i32) -> Self {
375 extern "C" {
376 fn CMTimeConvertScale(time: CMTime, newTimescale: i32, method: u32) -> CMTime;
377 }
378 unsafe { CMTimeConvertScale(self, new_timescale, 1) }
379 }
380}
381
382impl Default for CMTime {
383 fn default() -> Self {
384 Self::INVALID
385 }
386}
387
388impl fmt::Display for CMTime {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 if self.is_indefinite() {
391 f.write_str("indefinite")
392 } else if self.is_positive_infinity() {
393 f.write_str("+infinity")
394 } else if self.is_negative_infinity() {
395 f.write_str("-infinity")
396 } else if let Some(seconds) = self.as_seconds() {
397 write!(f, "{seconds:.3}s")
398 } else {
399 write!(f, "invalid")
400 }
401 }
402}
403
404#[repr(C)]
414#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
415pub struct CMTimeRange {
416 pub start: CMTime,
417 pub duration: CMTime,
418}
419
420impl CMTimeRange {
421 pub const INVALID: Self = Self {
423 start: CMTime::INVALID,
424 duration: CMTime::INVALID,
425 };
426
427 #[must_use]
429 pub const fn new(start: CMTime, duration: CMTime) -> Self {
430 Self { start, duration }
431 }
432
433 #[must_use]
435 pub fn end(&self) -> CMTime {
436 extern "C" {
437 fn CMTimeRangeGetEnd(range: CMTimeRange) -> CMTime;
438 }
439 unsafe { CMTimeRangeGetEnd(*self) }
440 }
441
442 #[must_use]
444 pub const fn is_valid(&self) -> bool {
445 self.start.is_valid() && self.duration.is_valid()
446 }
447
448 #[must_use]
450 pub fn contains_time(&self, time: CMTime) -> bool {
451 extern "C" {
452 fn CMTimeRangeContainsTime(range: CMTimeRange, time: CMTime) -> bool;
453 }
454 unsafe { CMTimeRangeContainsTime(*self, time) }
455 }
456
457 #[must_use]
459 pub fn contains_range(&self, other: Self) -> bool {
460 extern "C" {
461 fn CMTimeRangeContainsTimeRange(range: CMTimeRange, otherRange: CMTimeRange) -> bool;
462 }
463 unsafe { CMTimeRangeContainsTimeRange(*self, other) }
464 }
465
466 #[must_use]
468 pub fn intersection(&self, other: Self) -> Self {
469 extern "C" {
470 fn CMTimeRangeGetIntersection(
471 range: CMTimeRange,
472 otherRange: CMTimeRange,
473 ) -> CMTimeRange;
474 }
475 unsafe { CMTimeRangeGetIntersection(*self, other) }
476 }
477
478 #[must_use]
480 pub fn union(&self, other: Self) -> Self {
481 extern "C" {
482 fn CMTimeRangeGetUnion(range: CMTimeRange, otherRange: CMTimeRange) -> CMTimeRange;
483 }
484 unsafe { CMTimeRangeGetUnion(*self, other) }
485 }
486}
487
488impl fmt::Display for CMTimeRange {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 write!(
491 f,
492 "CMTimeRange(start: {}, duration: {})",
493 self.start, self.duration
494 )
495 }
496}
497
498pub struct CMClock {
503 ptr: *const c_void,
504}
505
506impl PartialEq for CMClock {
507 fn eq(&self, other: &Self) -> bool {
508 self.ptr == other.ptr
509 }
510}
511
512impl Eq for CMClock {}
513
514impl std::hash::Hash for CMClock {
515 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
516 self.ptr.hash(state);
517 }
518}
519
520impl CMClock {
521 #[must_use]
529 pub unsafe fn from_raw(ptr: *const c_void) -> Option<Self> {
530 if ptr.is_null() {
531 None
532 } else {
533 Some(Self { ptr })
534 }
535 }
536
537 #[must_use]
544 pub unsafe fn from_raw_borrowed(ptr: *const c_void) -> Option<Self> {
545 if ptr.is_null() {
546 None
547 } else {
548 extern "C" {
549 fn CFRetain(cf: *const c_void) -> *const c_void;
550 }
551 let retained = unsafe { CFRetain(ptr) };
552 unsafe { Self::from_raw(retained) }
553 }
554 }
555
556 #[must_use]
558 pub fn host_time_clock() -> Self {
559 extern "C" {
560 fn CMClockGetHostTimeClock() -> *const c_void;
561 fn CFRetain(cf: *const c_void) -> *const c_void;
562 }
563 let ptr = unsafe { CMClockGetHostTimeClock() };
564 assert!(!ptr.is_null(), "CMClockGetHostTimeClock returned NULL");
565 let retained = unsafe { CFRetain(ptr) };
566 Self { ptr: retained }
567 }
568
569 #[allow(dead_code)]
575 pub(crate) const unsafe fn from_ptr(ptr: *const c_void) -> Self {
576 Self { ptr }
577 }
578
579 #[must_use]
581 pub const fn as_ptr(&self) -> *const c_void {
582 self.ptr
583 }
584
585 #[must_use]
590 pub const fn time(&self) -> CMTime {
591 CMTime::INVALID
594 }
595}
596
597impl Drop for CMClock {
598 fn drop(&mut self) {
599 if !self.ptr.is_null() {
600 extern "C" {
602 fn CFRelease(cf: *const c_void);
603 }
604 unsafe {
605 CFRelease(self.ptr);
606 }
607 }
608 }
609}
610
611impl Clone for CMClock {
612 fn clone(&self) -> Self {
613 if self.ptr.is_null() {
614 Self {
615 ptr: std::ptr::null(),
616 }
617 } else {
618 extern "C" {
619 fn CFRetain(cf: *const c_void) -> *const c_void;
620 }
621 unsafe {
622 Self {
623 ptr: CFRetain(self.ptr),
624 }
625 }
626 }
627 }
628}
629
630impl std::fmt::Debug for CMClock {
631 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
632 f.debug_struct("CMClock").field("ptr", &self.ptr).finish()
633 }
634}
635
636impl fmt::Display for CMClock {
637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638 if self.ptr.is_null() {
639 write!(f, "CMClock(null)")
640 } else {
641 write!(f, "CMClock({:p})", self.ptr)
642 }
643 }
644}
645
646unsafe impl Send for CMClock {}
649unsafe impl Sync for CMClock {}