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]
115 pub const fn is_valid(&self) -> bool {
116 self.duration.is_valid()
117 && self.presentation_time_stamp.is_valid()
118 && self.decode_time_stamp.is_valid()
119 }
120
121 #[must_use]
123 pub const fn has_valid_presentation_time(&self) -> bool {
124 self.presentation_time_stamp.is_valid()
125 }
126
127 #[must_use]
129 pub const fn has_valid_decode_time(&self) -> bool {
130 self.decode_time_stamp.is_valid()
131 }
132
133 #[must_use]
135 pub const fn has_valid_duration(&self) -> bool {
136 self.duration.is_valid()
137 }
138
139 #[must_use]
141 pub fn presentation_seconds(&self) -> Option<f64> {
142 self.presentation_time_stamp.as_seconds()
143 }
144
145 #[must_use]
147 pub fn decode_seconds(&self) -> Option<f64> {
148 self.decode_time_stamp.as_seconds()
149 }
150
151 #[must_use]
153 pub fn duration_seconds(&self) -> Option<f64> {
154 self.duration.as_seconds()
155 }
156}
157
158impl Default for CMSampleTimingInfo {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl fmt::Display for CMSampleTimingInfo {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(
167 f,
168 "CMSampleTimingInfo(pts: {}, dts: {}, duration: {})",
169 self.presentation_time_stamp, self.decode_time_stamp, self.duration
170 )
171 }
172}
173
174impl CMTime {
175 pub const ZERO: Self = Self {
176 value: 0,
177 timescale: 0,
178 flags: 1,
179 epoch: 0,
180 };
181
182 pub const INVALID: Self = Self {
183 value: 0,
184 timescale: 0,
185 flags: 0,
186 epoch: 0,
187 };
188
189 #[must_use]
190 pub const fn new(value: i64, timescale: i32) -> Self {
191 Self {
192 value,
193 timescale,
194 flags: 1,
195 epoch: 0,
196 }
197 }
198
199 #[must_use]
200 pub const fn is_valid(&self) -> bool {
201 self.flags & 0x1 != 0
202 }
203
204 #[must_use]
206 pub const fn is_zero(&self) -> bool {
207 self.value == 0 && self.is_valid()
208 }
209
210 #[must_use]
212 pub const fn is_indefinite(&self) -> bool {
213 self.flags & 0x2 != 0
214 }
215
216 #[must_use]
218 pub const fn is_positive_infinity(&self) -> bool {
219 self.flags & 0x4 != 0
220 }
221
222 #[must_use]
224 pub const fn is_negative_infinity(&self) -> bool {
225 self.flags & 0x8 != 0
226 }
227
228 #[must_use]
230 pub const fn has_been_rounded(&self) -> bool {
231 self.flags & 0x10 != 0
232 }
233
234 #[must_use]
236 pub const fn equals(&self, other: &Self) -> bool {
237 if !self.is_valid() || !other.is_valid() {
238 return false;
239 }
240 self.value == other.value && self.timescale == other.timescale
241 }
242
243 #[must_use]
245 pub const fn positive_infinity() -> Self {
246 Self {
247 value: 0,
248 timescale: 0,
249 flags: 0x5, epoch: 0,
251 }
252 }
253
254 #[must_use]
256 pub const fn negative_infinity() -> Self {
257 Self {
258 value: 0,
259 timescale: 0,
260 flags: 0x9, epoch: 0,
262 }
263 }
264
265 #[must_use]
267 pub const fn indefinite() -> Self {
268 Self {
269 value: 0,
270 timescale: 0,
271 flags: 0x3, epoch: 0,
273 }
274 }
275
276 #[must_use]
277 pub fn as_seconds(&self) -> Option<f64> {
278 if self.is_valid() && self.timescale != 0 {
279 #[allow(clippy::cast_precision_loss)]
281 Some(self.value as f64 / f64::from(self.timescale))
282 } else {
283 None
284 }
285 }
286
287 #[must_use]
291 pub fn from_seconds(seconds: f64, preferred_timescale: i32) -> Self {
292 extern "C" {
293 fn CMTimeMakeWithSeconds(seconds: f64, preferredTimescale: i32) -> CMTime;
294 }
295 unsafe { CMTimeMakeWithSeconds(seconds, preferred_timescale) }
296 }
297
298 #[must_use]
301 #[allow(clippy::should_implement_trait)]
302 pub fn add(self, other: Self) -> Self {
303 extern "C" {
304 fn CMTimeAdd(addend1: CMTime, addend2: CMTime) -> CMTime;
305 }
306 unsafe { CMTimeAdd(self, other) }
307 }
308
309 #[must_use]
311 #[allow(clippy::should_implement_trait)]
312 pub fn subtract(self, other: Self) -> Self {
313 extern "C" {
314 fn CMTimeSubtract(minuend: CMTime, subtrahend: CMTime) -> CMTime;
315 }
316 unsafe { CMTimeSubtract(self, other) }
317 }
318
319 #[must_use]
321 pub fn multiply(self, multiplier: i32) -> Self {
322 extern "C" {
323 fn CMTimeMultiply(time: CMTime, multiplier: i32) -> CMTime;
324 }
325 unsafe { CMTimeMultiply(self, multiplier) }
326 }
327
328 #[must_use]
330 pub fn multiply_by_f64(self, factor: f64) -> Self {
331 extern "C" {
332 fn CMTimeMultiplyByFloat64(time: CMTime, multiplier: f64) -> CMTime;
333 }
334 unsafe { CMTimeMultiplyByFloat64(self, factor) }
335 }
336
337 #[must_use]
341 pub fn compare(self, other: Self) -> core::cmp::Ordering {
342 extern "C" {
343 fn CMTimeCompare(time1: CMTime, time2: CMTime) -> i32;
344 }
345 let c = unsafe { CMTimeCompare(self, other) };
346 c.cmp(&0)
347 }
348
349 #[must_use]
353 pub fn convert_scale(self, new_timescale: i32) -> Self {
354 extern "C" {
355 fn CMTimeConvertScale(time: CMTime, newTimescale: i32, method: u32) -> CMTime;
356 }
357 unsafe { CMTimeConvertScale(self, new_timescale, 0) }
358 }
359}
360
361impl Default for CMTime {
362 fn default() -> Self {
363 Self::INVALID
364 }
365}
366
367impl fmt::Display for CMTime {
368 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369 if let Some(seconds) = self.as_seconds() {
370 write!(f, "{seconds:.3}s")
371 } else {
372 write!(f, "invalid")
373 }
374 }
375}
376
377#[repr(C)]
387#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
388pub struct CMTimeRange {
389 pub start: CMTime,
390 pub duration: CMTime,
391}
392
393impl CMTimeRange {
394 pub const INVALID: Self = Self {
395 start: CMTime::INVALID,
396 duration: CMTime::INVALID,
397 };
398
399 #[must_use]
400 pub const fn new(start: CMTime, duration: CMTime) -> Self {
401 Self { start, duration }
402 }
403
404 #[must_use]
405 pub fn end(&self) -> CMTime {
406 extern "C" {
407 fn CMTimeRangeGetEnd(range: CMTimeRange) -> CMTime;
408 }
409 unsafe { CMTimeRangeGetEnd(*self) }
410 }
411
412 #[must_use]
413 pub const fn is_valid(&self) -> bool {
414 self.start.is_valid() && self.duration.is_valid()
415 }
416
417 #[must_use]
418 pub fn contains_time(&self, time: CMTime) -> bool {
419 extern "C" {
420 fn CMTimeRangeContainsTime(range: CMTimeRange, time: CMTime) -> bool;
421 }
422 unsafe { CMTimeRangeContainsTime(*self, time) }
423 }
424
425 #[must_use]
426 pub fn contains_range(&self, other: Self) -> bool {
427 extern "C" {
428 fn CMTimeRangeContainsTimeRange(range: CMTimeRange, otherRange: CMTimeRange) -> bool;
429 }
430 unsafe { CMTimeRangeContainsTimeRange(*self, other) }
431 }
432
433 #[must_use]
434 pub fn intersection(&self, other: Self) -> Self {
435 extern "C" {
436 fn CMTimeRangeGetIntersection(
437 range: CMTimeRange,
438 otherRange: CMTimeRange,
439 ) -> CMTimeRange;
440 }
441 unsafe { CMTimeRangeGetIntersection(*self, other) }
442 }
443
444 #[must_use]
445 pub fn union(&self, other: Self) -> Self {
446 extern "C" {
447 fn CMTimeRangeGetUnion(range: CMTimeRange, otherRange: CMTimeRange) -> CMTimeRange;
448 }
449 unsafe { CMTimeRangeGetUnion(*self, other) }
450 }
451}
452
453impl fmt::Display for CMTimeRange {
454 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
455 write!(
456 f,
457 "CMTimeRange(start: {}, duration: {})",
458 self.start, self.duration
459 )
460 }
461}
462
463pub struct CMClock {
468 ptr: *const c_void,
469}
470
471impl PartialEq for CMClock {
472 fn eq(&self, other: &Self) -> bool {
473 self.ptr == other.ptr
474 }
475}
476
477impl Eq for CMClock {}
478
479impl std::hash::Hash for CMClock {
480 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
481 self.ptr.hash(state);
482 }
483}
484
485impl CMClock {
486 #[must_use]
488 pub fn from_raw(ptr: *const c_void) -> Option<Self> {
489 if ptr.is_null() {
490 None
491 } else {
492 Some(Self { ptr })
493 }
494 }
495
496 #[must_use]
498 pub fn host_time_clock() -> Self {
499 extern "C" {
500 fn CMClockGetHostTimeClock() -> *const c_void;
501 fn CFRetain(cf: *const c_void) -> *const c_void;
502 }
503 let ptr = unsafe { CMClockGetHostTimeClock() };
504 assert!(!ptr.is_null(), "CMClockGetHostTimeClock returned NULL");
505 let retained = unsafe { CFRetain(ptr) };
506 Self { ptr: retained }
507 }
508
509 #[allow(dead_code)]
514 pub(crate) const fn from_ptr(ptr: *const c_void) -> Self {
515 Self { ptr }
516 }
517
518 #[must_use]
520 pub const fn as_ptr(&self) -> *const c_void {
521 self.ptr
522 }
523
524 #[must_use]
529 pub const fn time(&self) -> CMTime {
530 CMTime::INVALID
533 }
534}
535
536impl Drop for CMClock {
537 fn drop(&mut self) {
538 if !self.ptr.is_null() {
539 extern "C" {
541 fn CFRelease(cf: *const c_void);
542 }
543 unsafe {
544 CFRelease(self.ptr);
545 }
546 }
547 }
548}
549
550impl Clone for CMClock {
551 fn clone(&self) -> Self {
552 if self.ptr.is_null() {
553 Self {
554 ptr: std::ptr::null(),
555 }
556 } else {
557 extern "C" {
558 fn CFRetain(cf: *const c_void) -> *const c_void;
559 }
560 unsafe {
561 Self {
562 ptr: CFRetain(self.ptr),
563 }
564 }
565 }
566 }
567}
568
569impl std::fmt::Debug for CMClock {
570 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
571 f.debug_struct("CMClock").field("ptr", &self.ptr).finish()
572 }
573}
574
575impl fmt::Display for CMClock {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 if self.ptr.is_null() {
578 write!(f, "CMClock(null)")
579 } else {
580 write!(f, "CMClock({:p})", self.ptr)
581 }
582 }
583}
584
585unsafe impl Send for CMClock {}
588unsafe impl Sync for CMClock {}