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 pub const ZERO: Self = Self {
178 value: 0,
179 timescale: 0,
180 flags: 1,
181 epoch: 0,
182 };
183
184 pub const INVALID: Self = Self {
186 value: 0,
187 timescale: 0,
188 flags: 0,
189 epoch: 0,
190 };
191
192 #[must_use]
194 pub const fn new(value: i64, timescale: i32) -> Self {
195 Self {
196 value,
197 timescale,
198 flags: 1,
199 epoch: 0,
200 }
201 }
202
203 #[must_use]
205 pub const fn is_valid(&self) -> bool {
206 self.flags & 0x1 != 0
207 }
208
209 #[must_use]
211 pub const fn is_zero(&self) -> bool {
212 self.value == 0 && self.is_valid()
213 }
214
215 #[must_use]
217 pub const fn is_indefinite(&self) -> bool {
218 self.flags & 0x2 != 0
219 }
220
221 #[must_use]
223 pub const fn is_positive_infinity(&self) -> bool {
224 self.flags & 0x4 != 0
225 }
226
227 #[must_use]
229 pub const fn is_negative_infinity(&self) -> bool {
230 self.flags & 0x8 != 0
231 }
232
233 #[must_use]
235 pub const fn has_been_rounded(&self) -> bool {
236 self.flags & 0x10 != 0
237 }
238
239 #[must_use]
241 pub const fn equals(&self, other: &Self) -> bool {
242 if !self.is_valid() || !other.is_valid() {
243 return false;
244 }
245 self.value == other.value && self.timescale == other.timescale
246 }
247
248 #[must_use]
250 pub const fn positive_infinity() -> Self {
251 Self {
252 value: 0,
253 timescale: 0,
254 flags: 0x5, epoch: 0,
256 }
257 }
258
259 #[must_use]
261 pub const fn negative_infinity() -> Self {
262 Self {
263 value: 0,
264 timescale: 0,
265 flags: 0x9, epoch: 0,
267 }
268 }
269
270 #[must_use]
272 pub const fn indefinite() -> Self {
273 Self {
274 value: 0,
275 timescale: 0,
276 flags: 0x3, epoch: 0,
278 }
279 }
280
281 #[must_use]
283 pub fn as_seconds(&self) -> Option<f64> {
284 if self.is_valid() && self.timescale != 0 {
285 #[allow(clippy::cast_precision_loss)]
287 Some(self.value as f64 / f64::from(self.timescale))
288 } else {
289 None
290 }
291 }
292
293 #[must_use]
297 pub fn from_seconds(seconds: f64, preferred_timescale: i32) -> Self {
298 extern "C" {
299 fn CMTimeMakeWithSeconds(seconds: f64, preferredTimescale: i32) -> CMTime;
300 }
301 unsafe { CMTimeMakeWithSeconds(seconds, preferred_timescale) }
302 }
303
304 #[must_use]
307 #[allow(clippy::should_implement_trait)]
308 pub fn add(self, other: Self) -> Self {
309 extern "C" {
310 fn CMTimeAdd(addend1: CMTime, addend2: CMTime) -> CMTime;
311 }
312 unsafe { CMTimeAdd(self, other) }
313 }
314
315 #[must_use]
317 #[allow(clippy::should_implement_trait)]
318 pub fn subtract(self, other: Self) -> Self {
319 extern "C" {
320 fn CMTimeSubtract(minuend: CMTime, subtrahend: CMTime) -> CMTime;
321 }
322 unsafe { CMTimeSubtract(self, other) }
323 }
324
325 #[must_use]
327 pub fn multiply(self, multiplier: i32) -> Self {
328 extern "C" {
329 fn CMTimeMultiply(time: CMTime, multiplier: i32) -> CMTime;
330 }
331 unsafe { CMTimeMultiply(self, multiplier) }
332 }
333
334 #[must_use]
336 pub fn multiply_by_f64(self, factor: f64) -> Self {
337 extern "C" {
338 fn CMTimeMultiplyByFloat64(time: CMTime, multiplier: f64) -> CMTime;
339 }
340 unsafe { CMTimeMultiplyByFloat64(self, factor) }
341 }
342
343 #[must_use]
347 pub fn compare(self, other: Self) -> core::cmp::Ordering {
348 extern "C" {
349 fn CMTimeCompare(time1: CMTime, time2: CMTime) -> i32;
350 }
351 let c = unsafe { CMTimeCompare(self, other) };
352 c.cmp(&0)
353 }
354
355 #[must_use]
359 pub fn convert_scale(self, new_timescale: i32) -> Self {
360 extern "C" {
361 fn CMTimeConvertScale(time: CMTime, newTimescale: i32, method: u32) -> CMTime;
362 }
363 unsafe { CMTimeConvertScale(self, new_timescale, 0) }
364 }
365}
366
367impl Default for CMTime {
368 fn default() -> Self {
369 Self::INVALID
370 }
371}
372
373impl fmt::Display for CMTime {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 if let Some(seconds) = self.as_seconds() {
376 write!(f, "{seconds:.3}s")
377 } else {
378 write!(f, "invalid")
379 }
380 }
381}
382
383#[repr(C)]
393#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
394pub struct CMTimeRange {
395 pub start: CMTime,
396 pub duration: CMTime,
397}
398
399impl CMTimeRange {
400 pub const INVALID: Self = Self {
402 start: CMTime::INVALID,
403 duration: CMTime::INVALID,
404 };
405
406 #[must_use]
408 pub const fn new(start: CMTime, duration: CMTime) -> Self {
409 Self { start, duration }
410 }
411
412 #[must_use]
414 pub fn end(&self) -> CMTime {
415 extern "C" {
416 fn CMTimeRangeGetEnd(range: CMTimeRange) -> CMTime;
417 }
418 unsafe { CMTimeRangeGetEnd(*self) }
419 }
420
421 #[must_use]
423 pub const fn is_valid(&self) -> bool {
424 self.start.is_valid() && self.duration.is_valid()
425 }
426
427 #[must_use]
429 pub fn contains_time(&self, time: CMTime) -> bool {
430 extern "C" {
431 fn CMTimeRangeContainsTime(range: CMTimeRange, time: CMTime) -> bool;
432 }
433 unsafe { CMTimeRangeContainsTime(*self, time) }
434 }
435
436 #[must_use]
438 pub fn contains_range(&self, other: Self) -> bool {
439 extern "C" {
440 fn CMTimeRangeContainsTimeRange(range: CMTimeRange, otherRange: CMTimeRange) -> bool;
441 }
442 unsafe { CMTimeRangeContainsTimeRange(*self, other) }
443 }
444
445 #[must_use]
447 pub fn intersection(&self, other: Self) -> Self {
448 extern "C" {
449 fn CMTimeRangeGetIntersection(
450 range: CMTimeRange,
451 otherRange: CMTimeRange,
452 ) -> CMTimeRange;
453 }
454 unsafe { CMTimeRangeGetIntersection(*self, other) }
455 }
456
457 #[must_use]
459 pub fn union(&self, other: Self) -> Self {
460 extern "C" {
461 fn CMTimeRangeGetUnion(range: CMTimeRange, otherRange: CMTimeRange) -> CMTimeRange;
462 }
463 unsafe { CMTimeRangeGetUnion(*self, other) }
464 }
465}
466
467impl fmt::Display for CMTimeRange {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 write!(
470 f,
471 "CMTimeRange(start: {}, duration: {})",
472 self.start, self.duration
473 )
474 }
475}
476
477pub struct CMClock {
482 ptr: *const c_void,
483}
484
485impl PartialEq for CMClock {
486 fn eq(&self, other: &Self) -> bool {
487 self.ptr == other.ptr
488 }
489}
490
491impl Eq for CMClock {}
492
493impl std::hash::Hash for CMClock {
494 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
495 self.ptr.hash(state);
496 }
497}
498
499impl CMClock {
500 #[must_use]
502 pub fn from_raw(ptr: *const c_void) -> Option<Self> {
503 if ptr.is_null() {
504 None
505 } else {
506 Some(Self { ptr })
507 }
508 }
509
510 #[must_use]
512 pub fn host_time_clock() -> Self {
513 extern "C" {
514 fn CMClockGetHostTimeClock() -> *const c_void;
515 fn CFRetain(cf: *const c_void) -> *const c_void;
516 }
517 let ptr = unsafe { CMClockGetHostTimeClock() };
518 assert!(!ptr.is_null(), "CMClockGetHostTimeClock returned NULL");
519 let retained = unsafe { CFRetain(ptr) };
520 Self { ptr: retained }
521 }
522
523 #[allow(dead_code)]
528 pub(crate) const fn from_ptr(ptr: *const c_void) -> Self {
529 Self { ptr }
530 }
531
532 #[must_use]
534 pub const fn as_ptr(&self) -> *const c_void {
535 self.ptr
536 }
537
538 #[must_use]
543 pub const fn time(&self) -> CMTime {
544 CMTime::INVALID
547 }
548}
549
550impl Drop for CMClock {
551 fn drop(&mut self) {
552 if !self.ptr.is_null() {
553 extern "C" {
555 fn CFRelease(cf: *const c_void);
556 }
557 unsafe {
558 CFRelease(self.ptr);
559 }
560 }
561 }
562}
563
564impl Clone for CMClock {
565 fn clone(&self) -> Self {
566 if self.ptr.is_null() {
567 Self {
568 ptr: std::ptr::null(),
569 }
570 } else {
571 extern "C" {
572 fn CFRetain(cf: *const c_void) -> *const c_void;
573 }
574 unsafe {
575 Self {
576 ptr: CFRetain(self.ptr),
577 }
578 }
579 }
580 }
581}
582
583impl std::fmt::Debug for CMClock {
584 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
585 f.debug_struct("CMClock").field("ptr", &self.ptr).finish()
586 }
587}
588
589impl fmt::Display for CMClock {
590 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591 if self.ptr.is_null() {
592 write!(f, "CMClock(null)")
593 } else {
594 write!(f, "CMClock({:p})", self.ptr)
595 }
596 }
597}
598
599unsafe impl Send for CMClock {}
602unsafe impl Sync for CMClock {}