1use std::{
2 mem,
3 ptr::{null, null_mut},
4};
5
6use block::{Block, ConcreteBlock};
7use core_foundation::{
8 base::{kCFAllocatorDefault, Boolean, CFAllocatorRef, CFComparisonResult, CFGetTypeID, CFRetain, CFType, CFTypeID, CFTypeRef, OSStatus, TCFType},
9 declare_TCFType, impl_CFTypeDescription, impl_TCFType,
10 string::CFStringRef,
11};
12use libc::{c_void, size_t};
13
14use crate::{base::CMItemCount, time::CMTime};
15
16pub const kCMBufferQueueError_AllocationFailed: OSStatus = -12760;
17pub const kCMBufferQueueError_RequiredParameterMissing: OSStatus = -12761;
18pub const kCMBufferQueueError_InvalidCMBufferCallbacksStruct: OSStatus = -12762;
19pub const kCMBufferQueueError_EnqueueAfterEndOfData: OSStatus = -12763;
20pub const kCMBufferQueueError_QueueIsFull: OSStatus = -12764;
21pub const kCMBufferQueueError_BadTriggerDuration: OSStatus = -12765;
22pub const kCMBufferQueueError_CannotModifyQueueFromTriggerCallback: OSStatus = -12766;
23pub const kCMBufferQueueError_InvalidTriggerCondition: OSStatus = -12767;
24pub const kCMBufferQueueError_InvalidTriggerToken: OSStatus = -12768;
25pub const kCMBufferQueueError_InvalidBuffer: OSStatus = -12769;
26
27#[repr(C)]
28pub struct OpaqueCMBufferQueue(c_void);
29
30pub type CMBufferQueueRef = *mut OpaqueCMBufferQueue;
31
32pub type CMBufferRef = CFTypeRef;
33
34pub type CMBufferGetTimeCallback = extern "C" fn(buf: CMBufferRef, refcon: *mut c_void) -> CMTime;
35pub type CMBufferGetTimeHandler = *const Block<CMBufferRef, CMTime>;
36pub type CMBufferGetBooleanCallback = extern "C" fn(buf: CMBufferRef, refcon: *mut c_void) -> Boolean;
37pub type CMBufferGetBooleanHandler = *const Block<CMBufferRef, Boolean>;
38pub type CMBufferCompareCallback = extern "C" fn(buf1: CMBufferRef, buf2: CMBufferRef, refcon: *mut c_void) -> CFComparisonResult;
39pub type CMBufferCompareHandler = *const Block<(CMBufferRef, CMBufferRef), CFComparisonResult>;
40pub type CMBufferGetSizeCallback = extern "C" fn(buf: CMBufferRef, refcon: *mut c_void) -> size_t;
41pub type CMBufferGetSizeHandler = *const Block<CMBufferRef, size_t>;
42
43#[repr(C, align(4))]
44#[derive(Clone, Copy, Debug)]
45pub struct CMBufferCallbacks {
46 pub version: u32,
47 pub refcon: *mut c_void,
48 pub getDecodeTimeStamp: CMBufferGetTimeCallback,
49 pub getPresentationTimeStamp: CMBufferGetTimeCallback,
50 pub getDuration: CMBufferGetTimeCallback,
51 pub isDataReady: CMBufferGetBooleanCallback,
52 pub compare: CMBufferCompareCallback,
53 pub dataBecameReadyNotification: CFStringRef,
54 pub getSize: CMBufferGetSizeCallback,
55}
56
57extern "C" {
58 pub fn CMBufferQueueGetCallbacksForUnsortedSampleBuffers() -> *const CMBufferCallbacks;
59 pub fn CMBufferQueueGetCallbacksForSampleBuffersSortedByOutputPTS() -> *const CMBufferCallbacks;
60 pub fn CMBufferQueueCreate(
61 allocator: CFAllocatorRef,
62 capacity: CMItemCount,
63 callbacks: *const CMBufferCallbacks,
64 queueOut: *mut CMBufferQueueRef,
65 ) -> OSStatus;
66 pub fn CMBufferQueueGetTypeID() -> CFTypeID;
67 pub fn CMBufferQueueEnqueue(queue: CMBufferQueueRef, buf: CMBufferRef) -> OSStatus;
68 pub fn CMBufferQueueDequeueAndRetain(queue: CMBufferQueueRef) -> CMBufferRef;
69 pub fn CMBufferQueueDequeueIfDataReadyAndRetain(queue: CMBufferQueueRef) -> CMBufferRef;
70 pub fn CMBufferQueueGetHead(queue: CMBufferQueueRef) -> CMBufferRef;
71 pub fn CMBufferQueueCopyHead(queue: CMBufferQueueRef) -> CMBufferRef;
72 pub fn CMBufferQueueIsEmpty(queue: CMBufferQueueRef) -> Boolean;
73 pub fn CMBufferQueueMarkEndOfData(queue: CMBufferQueueRef) -> OSStatus;
74 pub fn CMBufferQueueContainsEndOfData(queue: CMBufferQueueRef) -> Boolean;
75 pub fn CMBufferQueueIsAtEndOfData(queue: CMBufferQueueRef) -> Boolean;
76 pub fn CMBufferQueueReset(queue: CMBufferQueueRef) -> OSStatus;
77 pub fn CMBufferQueueResetWithCallback(
78 queue: CMBufferQueueRef,
79 callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void),
80 refcon: *mut c_void,
81 ) -> OSStatus;
82 pub fn CMBufferQueueGetBufferCount(queue: CMBufferQueueRef) -> CMItemCount;
83 pub fn CMBufferQueueGetDuration(queue: CMBufferQueueRef) -> CMTime;
84 pub fn CMBufferQueueGetMinDecodeTimeStamp(queue: CMBufferQueueRef) -> CMTime;
85 pub fn CMBufferQueueGetFirstDecodeTimeStamp(queue: CMBufferQueueRef) -> CMTime;
86 pub fn CMBufferQueueGetMinPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
87 pub fn CMBufferQueueGetFirstPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
88 pub fn CMBufferQueueGetMaxPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
89 pub fn CMBufferQueueGetEndPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
90 pub fn CMBufferQueueGetTotalSize(queue: CMBufferQueueRef) -> size_t;
91}
92
93#[repr(C)]
94pub struct opaqueCMBufferQueueTriggerToken(c_void);
95
96pub type CMBufferQueueTriggerToken = *const opaqueCMBufferQueueTriggerToken;
97
98pub type CMBufferQueueTriggerCallback = extern "C" fn(triggerRefcon: *mut c_void, triggerToken: CMBufferQueueTriggerToken);
99pub type CMBufferQueueTriggerHandler = *const Block<(CMBufferQueueTriggerToken,), ()>;
100
101pub type CMBufferQueueTriggerCondition = i32;
102
103pub const kCMBufferQueueTrigger_WhenDurationBecomesLessThan: CMBufferQueueTriggerCondition = 1;
104pub const kCMBufferQueueTrigger_WhenDurationBecomesLessThanOrEqualTo: CMBufferQueueTriggerCondition = 2;
105pub const kCMBufferQueueTrigger_WhenDurationBecomesGreaterThan: CMBufferQueueTriggerCondition = 3;
106pub const kCMBufferQueueTrigger_WhenDurationBecomesGreaterThanOrEqualTo: CMBufferQueueTriggerCondition = 4;
107pub const kCMBufferQueueTrigger_WhenMinPresentationTimeStampChanges: CMBufferQueueTriggerCondition = 5;
108pub const kCMBufferQueueTrigger_WhenMaxPresentationTimeStampChanges: CMBufferQueueTriggerCondition = 6;
109pub const kCMBufferQueueTrigger_WhenDataBecomesReady: CMBufferQueueTriggerCondition = 7;
110pub const kCMBufferQueueTrigger_WhenEndOfDataReached: CMBufferQueueTriggerCondition = 8;
111pub const kCMBufferQueueTrigger_WhenReset: CMBufferQueueTriggerCondition = 9;
112pub const kCMBufferQueueTrigger_WhenBufferCountBecomesLessThan: CMBufferQueueTriggerCondition = 10;
113pub const kCMBufferQueueTrigger_WhenBufferCountBecomesGreaterThan: CMBufferQueueTriggerCondition = 11;
114pub const kCMBufferQueueTrigger_WhenDurationBecomesGreaterThanOrEqualToAndBufferCountBecomesGreaterThan: CMBufferQueueTriggerCondition = 12;
115
116extern "C" {
117 pub fn CMBufferQueueInstallTrigger(
118 queue: CMBufferQueueRef,
119 callback: CMBufferQueueTriggerCallback,
120 refcon: *mut c_void,
121 condition: CMBufferQueueTriggerCondition,
122 time: CMTime,
123 triggerTokenOut: *mut CMBufferQueueTriggerToken,
124 ) -> OSStatus;
125 pub fn CMBufferQueueInstallTriggerWithIntegerThreshold(
126 queue: CMBufferQueueRef,
127 callback: CMBufferQueueTriggerCallback,
128 refcon: *mut c_void,
129 condition: CMBufferQueueTriggerCondition,
130 threshold: CMItemCount,
131 triggerTokenOut: *mut CMBufferQueueTriggerToken,
132 ) -> OSStatus;
133 pub fn CMBufferQueueInstallTriggerHandler(
134 queue: CMBufferQueueRef,
135 condition: CMBufferQueueTriggerCondition,
136 time: CMTime,
137 triggerTokenOut: *mut CMBufferQueueTriggerToken,
138 handler: CMBufferQueueTriggerHandler,
139 ) -> OSStatus;
140 pub fn CMBufferQueueInstallTriggerHandlerWithIntegerThreshold(
141 queue: CMBufferQueueRef,
142 condition: CMBufferQueueTriggerCondition,
143 threshold: CMItemCount,
144 triggerTokenOut: *mut CMBufferQueueTriggerToken,
145 handler: CMBufferQueueTriggerHandler,
146 ) -> OSStatus;
147 pub fn CMBufferQueueRemoveTrigger(queue: CMBufferQueueRef, triggerToken: CMBufferQueueTriggerToken) -> OSStatus;
148 pub fn CMBufferQueueTestTrigger(queue: CMBufferQueueRef, triggerToken: CMBufferQueueTriggerToken) -> Boolean;
149 pub fn CMBufferQueueCallForEachBuffer(
150 queue: CMBufferQueueRef,
151 callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void) -> OSStatus,
152 refcon: *mut c_void,
153 ) -> OSStatus;
154}
155
156pub type CMBufferValidationCallback = extern "C" fn(queue: CMBufferQueueRef, buf: CMBufferRef, validationRefCon: *mut c_void) -> OSStatus;
157pub type CMBufferValidationHandler = *const Block<(CMBufferQueueRef, CMBufferRef), OSStatus>;
158
159extern "C" {
160 pub fn CMBufferQueueSetValidationCallback(queue: CMBufferQueueRef, callback: CMBufferValidationCallback, refCon: *mut c_void) -> OSStatus;
161 pub fn CMBufferQueueSetValidationHandler(queue: CMBufferQueueRef, handler: CMBufferValidationHandler) -> OSStatus;
162}
163
164declare_TCFType! {
165 CMBuffer, CMBufferRef
166}
167impl_CFTypeDescription!(CMBuffer);
168
169impl CMBuffer {
170 #[inline]
171 pub fn as_concrete_TypeRef(&self) -> CMBufferRef {
172 self.0
173 }
174
175 #[inline]
176 pub fn as_CFType(&self) -> CFType {
177 unsafe { CFType::wrap_under_get_rule(self.as_concrete_TypeRef()) }
178 }
179
180 #[inline]
181 pub fn as_CFTypeRef(&self) -> CFTypeRef {
182 self.as_concrete_TypeRef() as CFTypeRef
183 }
184
185 #[inline]
186 pub fn into_CFType(self) -> CFType
187 where
188 Self: Sized,
189 {
190 let reference = self.as_CFTypeRef();
191 mem::forget(self);
192 unsafe { CFType::wrap_under_create_rule(reference) }
193 }
194
195 #[inline]
196 pub unsafe fn wrap_under_create_rule(reference: CMBufferRef) -> Self {
197 CMBuffer(reference)
198 }
199
200 #[inline]
201 pub unsafe fn wrap_under_get_rule(reference: CMBufferRef) -> Self {
202 let reference = CFRetain(reference);
203 CMBuffer(reference)
204 }
205
206 #[inline]
207 pub fn type_of(&self) -> CFTypeID {
208 unsafe { CFGetTypeID(self.as_CFTypeRef()) }
209 }
210
211 #[inline]
212 pub fn instance_of<T: TCFType>(&self) -> bool {
213 self.type_of() == T::type_id()
214 }
215}
216
217declare_TCFType! {
218 CMBufferQueue, CMBufferQueueRef
219}
220impl_TCFType!(CMBufferQueue, CMBufferQueueRef, CMBufferQueueGetTypeID);
221impl_CFTypeDescription!(CMBufferQueue);
222
223impl CMBufferQueue {
224 #[inline]
225 pub fn new(callbacks: &[CMBufferCallbacks]) -> Result<CMBufferQueue, OSStatus> {
226 unsafe {
227 let mut queue: CMBufferQueueRef = null_mut();
228 let status = CMBufferQueueCreate(kCFAllocatorDefault, callbacks.len() as CMItemCount, callbacks.as_ptr(), &mut queue);
229 if status == 0 {
230 Ok(TCFType::wrap_under_create_rule(queue))
231 } else {
232 Err(status)
233 }
234 }
235 }
236
237 #[inline]
238 pub fn enqueue(&self, buf: &CMBuffer) -> Result<(), OSStatus> {
239 unsafe {
240 let status = CMBufferQueueEnqueue(self.as_concrete_TypeRef(), buf.as_concrete_TypeRef());
241 if status == 0 {
242 Ok(())
243 } else {
244 Err(status)
245 }
246 }
247 }
248
249 #[inline]
250 pub fn dequeue_and_retain(&self) -> Option<CMBuffer> {
251 unsafe {
252 let buf = CMBufferQueueDequeueAndRetain(self.as_concrete_TypeRef());
253 if buf.is_null() {
254 None
255 } else {
256 Some(CMBuffer::wrap_under_create_rule(buf))
257 }
258 }
259 }
260
261 #[inline]
262 pub fn dequeue_if_data_ready_and_retain(&self) -> Option<CMBuffer> {
263 unsafe {
264 let buf = CMBufferQueueDequeueIfDataReadyAndRetain(self.as_concrete_TypeRef());
265 if buf.is_null() {
266 None
267 } else {
268 Some(CMBuffer::wrap_under_create_rule(buf))
269 }
270 }
271 }
272
273 #[inline]
274 pub fn get_head(&self) -> Option<CMBuffer> {
275 unsafe {
276 let buf = CMBufferQueueGetHead(self.as_concrete_TypeRef());
277 if buf.is_null() {
278 None
279 } else {
280 Some(CMBuffer::wrap_under_get_rule(buf))
281 }
282 }
283 }
284
285 #[inline]
286 pub fn copy_head(&self) -> Option<CMBuffer> {
287 unsafe {
288 let buf = CMBufferQueueCopyHead(self.as_concrete_TypeRef());
289 if buf.is_null() {
290 None
291 } else {
292 Some(CMBuffer::wrap_under_create_rule(buf))
293 }
294 }
295 }
296
297 #[inline]
298 pub fn is_empty(&self) -> bool {
299 unsafe { CMBufferQueueIsEmpty(self.as_concrete_TypeRef()) != 0 }
300 }
301
302 #[inline]
303 pub fn mark_end_of_data(&self) -> Result<(), OSStatus> {
304 unsafe {
305 let status = CMBufferQueueMarkEndOfData(self.as_concrete_TypeRef());
306 if status == 0 {
307 Ok(())
308 } else {
309 Err(status)
310 }
311 }
312 }
313
314 #[inline]
315 pub fn contains_end_of_data(&self) -> bool {
316 unsafe { CMBufferQueueContainsEndOfData(self.as_concrete_TypeRef()) != 0 }
317 }
318
319 #[inline]
320 pub fn is_at_end_of_data(&self) -> bool {
321 unsafe { CMBufferQueueIsAtEndOfData(self.as_concrete_TypeRef()) != 0 }
322 }
323
324 #[inline]
325 pub fn reset(&self) -> Result<(), OSStatus> {
326 unsafe {
327 let status = CMBufferQueueReset(self.as_concrete_TypeRef());
328 if status == 0 {
329 Ok(())
330 } else {
331 Err(status)
332 }
333 }
334 }
335
336 #[inline]
337 pub unsafe fn reset_with_callback(
338 &self,
339 callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void),
340 refcon: *mut c_void,
341 ) -> Result<(), OSStatus> {
342 unsafe {
343 let status = CMBufferQueueResetWithCallback(self.as_concrete_TypeRef(), callback, refcon);
344 if status == 0 {
345 Ok(())
346 } else {
347 Err(status)
348 }
349 }
350 }
351
352 #[inline]
353 pub fn get_buffer_count(&self) -> CMItemCount {
354 unsafe { CMBufferQueueGetBufferCount(self.as_concrete_TypeRef()) }
355 }
356
357 #[inline]
358 pub fn get_duration(&self) -> CMTime {
359 unsafe { CMBufferQueueGetDuration(self.as_concrete_TypeRef()) }
360 }
361
362 #[inline]
363 pub fn get_min_decode_time_stamp(&self) -> CMTime {
364 unsafe { CMBufferQueueGetMinDecodeTimeStamp(self.as_concrete_TypeRef()) }
365 }
366
367 #[inline]
368 pub fn get_first_decode_time_stamp(&self) -> CMTime {
369 unsafe { CMBufferQueueGetFirstDecodeTimeStamp(self.as_concrete_TypeRef()) }
370 }
371
372 #[inline]
373 pub fn get_min_presentation_time_stamp(&self) -> CMTime {
374 unsafe { CMBufferQueueGetMinPresentationTimeStamp(self.as_concrete_TypeRef()) }
375 }
376
377 #[inline]
378 pub fn get_first_presentation_time_stamp(&self) -> CMTime {
379 unsafe { CMBufferQueueGetFirstPresentationTimeStamp(self.as_concrete_TypeRef()) }
380 }
381
382 #[inline]
383 pub fn get_max_presentation_time_stamp(&self) -> CMTime {
384 unsafe { CMBufferQueueGetMaxPresentationTimeStamp(self.as_concrete_TypeRef()) }
385 }
386
387 #[inline]
388 pub fn get_end_presentation_time_stamp(&self) -> CMTime {
389 unsafe { CMBufferQueueGetEndPresentationTimeStamp(self.as_concrete_TypeRef()) }
390 }
391
392 #[inline]
393 pub fn get_total_size(&self) -> size_t {
394 unsafe { CMBufferQueueGetTotalSize(self.as_concrete_TypeRef()) }
395 }
396
397 pub unsafe fn install_trigger(
398 &self,
399 callback: CMBufferQueueTriggerCallback,
400 refcon: *mut c_void,
401 condition: CMBufferQueueTriggerCondition,
402 time: CMTime,
403 ) -> Result<CMBufferQueueTriggerToken, OSStatus> {
404 unsafe {
405 let mut token = null();
406 let status = CMBufferQueueInstallTrigger(self.as_concrete_TypeRef(), callback, refcon, condition, time, &mut token);
407 if status == 0 {
408 Ok(token)
409 } else {
410 Err(status)
411 }
412 }
413 }
414
415 pub unsafe fn install_trigger_with_integer_threshold(
416 &self,
417 callback: CMBufferQueueTriggerCallback,
418 refcon: *mut c_void,
419 condition: CMBufferQueueTriggerCondition,
420 threshold: CMItemCount,
421 ) -> Result<CMBufferQueueTriggerToken, OSStatus> {
422 unsafe {
423 let mut token = null();
424 let status =
425 CMBufferQueueInstallTriggerWithIntegerThreshold(self.as_concrete_TypeRef(), callback, refcon, condition, threshold, &mut token);
426 if status == 0 {
427 Ok(token)
428 } else {
429 Err(status)
430 }
431 }
432 }
433
434 pub fn install_trigger_closure<F>(
435 &self,
436 condition: CMBufferQueueTriggerCondition,
437 time: CMTime,
438 closure: Option<F>,
439 ) -> Result<CMBufferQueueTriggerToken, OSStatus>
440 where
441 F: Fn(CMBufferQueueTriggerToken) + 'static,
442 {
443 let mut token = null();
444 let handler = closure.map(|closure| {
445 ConcreteBlock::new(move |trigger_token: CMBufferQueueTriggerToken| {
446 closure(trigger_token);
447 })
448 .copy()
449 });
450 let status = unsafe {
451 CMBufferQueueInstallTriggerHandler(self.as_concrete_TypeRef(), condition, time, &mut token, handler.as_ref().map_or(null(), |h| &**h))
452 };
453 if status == 0 {
454 Ok(token)
455 } else {
456 Err(status)
457 }
458 }
459
460 pub fn install_trigger_with_integer_threshold_closure<F>(
461 &self,
462 condition: CMBufferQueueTriggerCondition,
463 threshold: CMItemCount,
464 closure: Option<F>,
465 ) -> Result<CMBufferQueueTriggerToken, OSStatus>
466 where
467 F: Fn(CMBufferQueueTriggerToken) + 'static,
468 {
469 let mut token = null();
470 let handler = closure.map(|closure| {
471 ConcreteBlock::new(move |trigger_token: CMBufferQueueTriggerToken| {
472 closure(trigger_token);
473 })
474 .copy()
475 });
476 let status = unsafe {
477 CMBufferQueueInstallTriggerHandlerWithIntegerThreshold(
478 self.as_concrete_TypeRef(),
479 condition,
480 threshold,
481 &mut token,
482 handler.as_ref().map_or(null(), |h| &**h),
483 )
484 };
485 if status == 0 {
486 Ok(token)
487 } else {
488 Err(status)
489 }
490 }
491
492 #[inline]
493 pub fn remove_trigger(&self, token: CMBufferQueueTriggerToken) -> Result<(), OSStatus> {
494 unsafe {
495 let status = CMBufferQueueRemoveTrigger(self.as_concrete_TypeRef(), token);
496 if status == 0 {
497 Ok(())
498 } else {
499 Err(status)
500 }
501 }
502 }
503
504 #[inline]
505 pub fn test_trigger(&self, token: CMBufferQueueTriggerToken) -> bool {
506 unsafe { CMBufferQueueTestTrigger(self.as_concrete_TypeRef(), token) != 0 }
507 }
508
509 #[inline]
510 pub unsafe fn call_for_each_buffer(
511 &self,
512 callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void) -> OSStatus,
513 refcon: *mut c_void,
514 ) -> Result<(), OSStatus> {
515 unsafe {
516 let status = CMBufferQueueCallForEachBuffer(self.as_concrete_TypeRef(), callback, refcon);
517 if status == 0 {
518 Ok(())
519 } else {
520 Err(status)
521 }
522 }
523 }
524
525 pub unsafe fn set_validation_callback(&self, callback: CMBufferValidationCallback, refcon: *mut c_void) -> Result<(), OSStatus> {
526 unsafe {
527 let status = CMBufferQueueSetValidationCallback(self.as_concrete_TypeRef(), callback, refcon);
528 if status == 0 {
529 Ok(())
530 } else {
531 Err(status)
532 }
533 }
534 }
535
536 pub fn set_validation_closure<F>(&self, closure: Option<F>) -> Result<(), OSStatus>
537 where
538 F: Fn(CMBufferQueue, CMBuffer) -> OSStatus + 'static,
539 {
540 let handler = closure.map(|closure| {
541 ConcreteBlock::new(move |queue: CMBufferQueueRef, buf: CMBufferRef| {
542 let queue = unsafe { CMBufferQueue::wrap_under_get_rule(queue) };
543 let buf = unsafe { CMBuffer::wrap_under_get_rule(buf) };
544 closure(queue, buf)
545 })
546 .copy()
547 });
548
549 let status = unsafe { CMBufferQueueSetValidationHandler(self.as_concrete_TypeRef(), handler.as_ref().map_or(null(), |h| &**h)) };
550 if status == 0 {
551 Ok(())
552 } else {
553 Err(status)
554 }
555 }
556}