1#![allow(non_snake_case)]
2#![allow(non_camel_case_types)]
3
4mod bindings;
5
6#[cfg(target_os = "macos")]
7use core_foundation::{
8 base::{CFTypeID, TCFType},
9 declare_TCFType, impl_CFTypeDescription, impl_TCFType,
10};
11use std::ffi::c_void;
12
13#[cfg(target_os = "macos")]
14pub mod io_surface {
15 use super::*;
16
17 #[repr(C)]
18 pub struct __IOSurface(c_void);
19 // The ref type must be a pointer to the underlying struct.
20 pub type IOSurfaceRef = *const __IOSurface;
21
22 declare_TCFType!(IOSurface, IOSurfaceRef);
23 impl_TCFType!(IOSurface, IOSurfaceRef, IOSurfaceGetTypeID);
24 impl_CFTypeDescription!(IOSurface);
25
26 #[link(name = "IOSurface", kind = "framework")]
27 extern "C" {
28 fn IOSurfaceGetTypeID() -> CFTypeID;
29 }
30}
31
32#[cfg(target_os = "macos")]
33pub mod core_video {
34 #![allow(non_snake_case)]
35
36 use super::*;
37 pub use crate::bindings::{
38 kCVPixelFormatType_32BGRA, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
39 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, kCVPixelFormatType_420YpCbCr8Planar,
40 };
41 use crate::bindings::{kCVReturnSuccess, CVReturn, OSType};
42 use anyhow::{anyhow, Result};
43 use core_foundation::{
44 base::kCFAllocatorDefault, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef,
45 };
46 use foreign_types::ForeignTypeRef;
47 use io_surface::{IOSurface, IOSurfaceRef};
48 use metal::{MTLDevice, MTLPixelFormat};
49 use std::ptr;
50
51 #[repr(C)]
52 pub struct __CVImageBuffer(c_void);
53 // The ref type must be a pointer to the underlying struct.
54 pub type CVImageBufferRef = *const __CVImageBuffer;
55
56 declare_TCFType!(CVImageBuffer, CVImageBufferRef);
57 impl_TCFType!(CVImageBuffer, CVImageBufferRef, CVImageBufferGetTypeID);
58 impl_CFTypeDescription!(CVImageBuffer);
59
60 impl CVImageBuffer {
61 pub fn io_surface(&self) -> IOSurface {
62 unsafe {
63 IOSurface::wrap_under_get_rule(CVPixelBufferGetIOSurface(
64 self.as_concrete_TypeRef(),
65 ))
66 }
67 }
68
69 pub fn width(&self) -> usize {
70 unsafe { CVPixelBufferGetWidth(self.as_concrete_TypeRef()) }
71 }
72
73 pub fn height(&self) -> usize {
74 unsafe { CVPixelBufferGetHeight(self.as_concrete_TypeRef()) }
75 }
76
77 pub fn plane_width(&self, plane: usize) -> usize {
78 unsafe { CVPixelBufferGetWidthOfPlane(self.as_concrete_TypeRef(), plane) }
79 }
80
81 pub fn plane_height(&self, plane: usize) -> usize {
82 unsafe { CVPixelBufferGetHeightOfPlane(self.as_concrete_TypeRef(), plane) }
83 }
84
85 pub fn pixel_format_type(&self) -> OSType {
86 unsafe { CVPixelBufferGetPixelFormatType(self.as_concrete_TypeRef()) }
87 }
88 }
89
90 #[link(name = "CoreVideo", kind = "framework")]
91 extern "C" {
92 fn CVImageBufferGetTypeID() -> CFTypeID;
93 fn CVPixelBufferGetIOSurface(buffer: CVImageBufferRef) -> IOSurfaceRef;
94 fn CVPixelBufferGetWidth(buffer: CVImageBufferRef) -> usize;
95 fn CVPixelBufferGetHeight(buffer: CVImageBufferRef) -> usize;
96 fn CVPixelBufferGetWidthOfPlane(buffer: CVImageBufferRef, plane: usize) -> usize;
97 fn CVPixelBufferGetHeightOfPlane(buffer: CVImageBufferRef, plane: usize) -> usize;
98 fn CVPixelBufferGetPixelFormatType(buffer: CVImageBufferRef) -> OSType;
99 }
100
101 #[repr(C)]
102 pub struct __CVMetalTextureCache(c_void);
103 pub type CVMetalTextureCacheRef = *const __CVMetalTextureCache;
104
105 declare_TCFType!(CVMetalTextureCache, CVMetalTextureCacheRef);
106 impl_TCFType!(
107 CVMetalTextureCache,
108 CVMetalTextureCacheRef,
109 CVMetalTextureCacheGetTypeID
110 );
111 impl_CFTypeDescription!(CVMetalTextureCache);
112
113 impl CVMetalTextureCache {
114 /// # Safety
115 ///
116 /// metal_device must be valid according to CVMetalTextureCacheCreate
117 pub unsafe fn new(metal_device: *mut MTLDevice) -> Result<Self> {
118 let mut this = ptr::null();
119 let result = CVMetalTextureCacheCreate(
120 kCFAllocatorDefault,
121 ptr::null(),
122 metal_device,
123 ptr::null(),
124 &mut this,
125 );
126 if result == kCVReturnSuccess {
127 Ok(CVMetalTextureCache::wrap_under_create_rule(this))
128 } else {
129 Err(anyhow!("could not create texture cache, code: {}", result))
130 }
131 }
132
133 /// # Safety
134 ///
135 /// The arguments to this function must be valid according to CVMetalTextureCacheCreateTextureFromImage
136 pub unsafe fn create_texture_from_image(
137 &self,
138 source: CVImageBufferRef,
139 texture_attributes: CFDictionaryRef,
140 pixel_format: MTLPixelFormat,
141 width: usize,
142 height: usize,
143 plane_index: usize,
144 ) -> Result<CVMetalTexture> {
145 let mut this = ptr::null();
146 let result = CVMetalTextureCacheCreateTextureFromImage(
147 kCFAllocatorDefault,
148 self.as_concrete_TypeRef(),
149 source,
150 texture_attributes,
151 pixel_format,
152 width,
153 height,
154 plane_index,
155 &mut this,
156 );
157 if result == kCVReturnSuccess {
158 Ok(CVMetalTexture::wrap_under_create_rule(this))
159 } else {
160 Err(anyhow!("could not create texture, code: {}", result))
161 }
162 }
163 }
164
165 #[link(name = "CoreVideo", kind = "framework")]
166 extern "C" {
167 fn CVMetalTextureCacheGetTypeID() -> CFTypeID;
168 fn CVMetalTextureCacheCreate(
169 allocator: CFAllocatorRef,
170 cache_attributes: CFDictionaryRef,
171 metal_device: *const MTLDevice,
172 texture_attributes: CFDictionaryRef,
173 cache_out: *mut CVMetalTextureCacheRef,
174 ) -> CVReturn;
175 fn CVMetalTextureCacheCreateTextureFromImage(
176 allocator: CFAllocatorRef,
177 texture_cache: CVMetalTextureCacheRef,
178 source_image: CVImageBufferRef,
179 texture_attributes: CFDictionaryRef,
180 pixel_format: MTLPixelFormat,
181 width: usize,
182 height: usize,
183 plane_index: usize,
184 texture_out: *mut CVMetalTextureRef,
185 ) -> CVReturn;
186 }
187
188 #[repr(C)]
189 pub struct __CVMetalTexture(c_void);
190 pub type CVMetalTextureRef = *const __CVMetalTexture;
191
192 declare_TCFType!(CVMetalTexture, CVMetalTextureRef);
193 impl_TCFType!(CVMetalTexture, CVMetalTextureRef, CVMetalTextureGetTypeID);
194 impl_CFTypeDescription!(CVMetalTexture);
195
196 impl CVMetalTexture {
197 pub fn as_texture_ref(&self) -> &metal::TextureRef {
198 unsafe {
199 let texture = CVMetalTextureGetTexture(self.as_concrete_TypeRef());
200 metal::TextureRef::from_ptr(texture as *mut _)
201 }
202 }
203 }
204
205 #[link(name = "CoreVideo", kind = "framework")]
206 extern "C" {
207 fn CVMetalTextureGetTypeID() -> CFTypeID;
208 fn CVMetalTextureGetTexture(texture: CVMetalTextureRef) -> *mut c_void;
209 }
210}
211
212#[cfg(target_os = "macos")]
213pub mod core_media {
214 #![allow(non_snake_case)]
215
216 pub use crate::bindings::{
217 kCMSampleAttachmentKey_NotSync, kCMTimeInvalid, kCMVideoCodecType_H264, CMItemIndex,
218 CMSampleTimingInfo, CMTime, CMTimeMake, CMVideoCodecType,
219 };
220 use crate::core_video::{CVImageBuffer, CVImageBufferRef};
221 use anyhow::{anyhow, Result};
222 use core_foundation::{
223 array::{CFArray, CFArrayRef},
224 base::{CFTypeID, OSStatus, TCFType},
225 declare_TCFType,
226 dictionary::CFDictionary,
227 impl_CFTypeDescription, impl_TCFType,
228 string::CFString,
229 };
230 use std::{ffi::c_void, ptr};
231
232 #[repr(C)]
233 pub struct __CMSampleBuffer(c_void);
234 // The ref type must be a pointer to the underlying struct.
235 pub type CMSampleBufferRef = *const __CMSampleBuffer;
236
237 declare_TCFType!(CMSampleBuffer, CMSampleBufferRef);
238 impl_TCFType!(CMSampleBuffer, CMSampleBufferRef, CMSampleBufferGetTypeID);
239 impl_CFTypeDescription!(CMSampleBuffer);
240
241 impl CMSampleBuffer {
242 pub fn attachments(&self) -> Vec<CFDictionary<CFString>> {
243 unsafe {
244 let attachments =
245 CMSampleBufferGetSampleAttachmentsArray(self.as_concrete_TypeRef(), true);
246 CFArray::<CFDictionary>::wrap_under_get_rule(attachments)
247 .into_iter()
248 .map(|attachments| {
249 CFDictionary::wrap_under_get_rule(attachments.as_concrete_TypeRef())
250 })
251 .collect()
252 }
253 }
254
255 pub fn image_buffer(&self) -> CVImageBuffer {
256 unsafe {
257 CVImageBuffer::wrap_under_get_rule(CMSampleBufferGetImageBuffer(
258 self.as_concrete_TypeRef(),
259 ))
260 }
261 }
262
263 pub fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo> {
264 unsafe {
265 let mut timing_info = CMSampleTimingInfo {
266 duration: kCMTimeInvalid,
267 presentationTimeStamp: kCMTimeInvalid,
268 decodeTimeStamp: kCMTimeInvalid,
269 };
270 let result = CMSampleBufferGetSampleTimingInfo(
271 self.as_concrete_TypeRef(),
272 index as CMItemIndex,
273 &mut timing_info,
274 );
275
276 if result == 0 {
277 Ok(timing_info)
278 } else {
279 Err(anyhow!("error getting sample timing info, code {}", result))
280 }
281 }
282 }
283
284 pub fn format_description(&self) -> CMFormatDescription {
285 unsafe {
286 CMFormatDescription::wrap_under_get_rule(CMSampleBufferGetFormatDescription(
287 self.as_concrete_TypeRef(),
288 ))
289 }
290 }
291
292 pub fn data(&self) -> CMBlockBuffer {
293 unsafe {
294 CMBlockBuffer::wrap_under_get_rule(CMSampleBufferGetDataBuffer(
295 self.as_concrete_TypeRef(),
296 ))
297 }
298 }
299 }
300
301 #[link(name = "CoreMedia", kind = "framework")]
302 extern "C" {
303 fn CMSampleBufferGetTypeID() -> CFTypeID;
304 fn CMSampleBufferGetSampleAttachmentsArray(
305 buffer: CMSampleBufferRef,
306 create_if_necessary: bool,
307 ) -> CFArrayRef;
308 fn CMSampleBufferGetImageBuffer(buffer: CMSampleBufferRef) -> CVImageBufferRef;
309 fn CMSampleBufferGetSampleTimingInfo(
310 buffer: CMSampleBufferRef,
311 index: CMItemIndex,
312 timing_info_out: *mut CMSampleTimingInfo,
313 ) -> OSStatus;
314 fn CMSampleBufferGetFormatDescription(buffer: CMSampleBufferRef) -> CMFormatDescriptionRef;
315 fn CMSampleBufferGetDataBuffer(sample_buffer: CMSampleBufferRef) -> CMBlockBufferRef;
316 }
317
318 #[repr(C)]
319 pub struct __CMFormatDescription(c_void);
320 pub type CMFormatDescriptionRef = *const __CMFormatDescription;
321
322 declare_TCFType!(CMFormatDescription, CMFormatDescriptionRef);
323 impl_TCFType!(
324 CMFormatDescription,
325 CMFormatDescriptionRef,
326 CMFormatDescriptionGetTypeID
327 );
328 impl_CFTypeDescription!(CMFormatDescription);
329
330 impl CMFormatDescription {
331 pub fn h264_parameter_set_count(&self) -> usize {
332 unsafe {
333 let mut count = 0;
334 let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
335 self.as_concrete_TypeRef(),
336 0,
337 ptr::null_mut(),
338 ptr::null_mut(),
339 &mut count,
340 ptr::null_mut(),
341 );
342 assert_eq!(result, 0);
343 count
344 }
345 }
346
347 pub fn h264_parameter_set_at_index(&self, index: usize) -> Result<&[u8]> {
348 unsafe {
349 let mut bytes = ptr::null();
350 let mut len = 0;
351 let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
352 self.as_concrete_TypeRef(),
353 index,
354 &mut bytes,
355 &mut len,
356 ptr::null_mut(),
357 ptr::null_mut(),
358 );
359 if result == 0 {
360 Ok(std::slice::from_raw_parts(bytes, len))
361 } else {
362 Err(anyhow!("error getting parameter set, code: {}", result))
363 }
364 }
365 }
366 }
367
368 #[link(name = "CoreMedia", kind = "framework")]
369 extern "C" {
370 fn CMFormatDescriptionGetTypeID() -> CFTypeID;
371 fn CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
372 video_desc: CMFormatDescriptionRef,
373 parameter_set_index: usize,
374 parameter_set_pointer_out: *mut *const u8,
375 parameter_set_size_out: *mut usize,
376 parameter_set_count_out: *mut usize,
377 NALUnitHeaderLengthOut: *mut isize,
378 ) -> OSStatus;
379 }
380
381 #[repr(C)]
382 pub struct __CMBlockBuffer(c_void);
383 pub type CMBlockBufferRef = *const __CMBlockBuffer;
384
385 declare_TCFType!(CMBlockBuffer, CMBlockBufferRef);
386 impl_TCFType!(CMBlockBuffer, CMBlockBufferRef, CMBlockBufferGetTypeID);
387 impl_CFTypeDescription!(CMBlockBuffer);
388
389 impl CMBlockBuffer {
390 pub fn bytes(&self) -> &[u8] {
391 unsafe {
392 let mut bytes = ptr::null();
393 let mut len = 0;
394 let result = CMBlockBufferGetDataPointer(
395 self.as_concrete_TypeRef(),
396 0,
397 &mut 0,
398 &mut len,
399 &mut bytes,
400 );
401 assert!(result == 0, "could not get block buffer data");
402 std::slice::from_raw_parts(bytes, len)
403 }
404 }
405 }
406
407 #[link(name = "CoreMedia", kind = "framework")]
408 extern "C" {
409 fn CMBlockBufferGetTypeID() -> CFTypeID;
410 fn CMBlockBufferGetDataPointer(
411 buffer: CMBlockBufferRef,
412 offset: usize,
413 length_at_offset_out: *mut usize,
414 total_length_out: *mut usize,
415 data_pointer_out: *mut *const u8,
416 ) -> OSStatus;
417 }
418}
419
420#[cfg(target_os = "macos")]
421pub mod video_toolbox {
422 #![allow(non_snake_case)]
423
424 use super::*;
425 use crate::{
426 core_media::{CMSampleBufferRef, CMTime, CMVideoCodecType},
427 core_video::CVImageBufferRef,
428 };
429 use anyhow::{anyhow, Result};
430 pub use bindings::VTEncodeInfoFlags;
431 use core_foundation::{base::OSStatus, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef};
432 use std::ptr;
433
434 #[repr(C)]
435 pub struct __VTCompressionSession(c_void);
436 // The ref type must be a pointer to the underlying struct.
437 pub type VTCompressionSessionRef = *const __VTCompressionSession;
438
439 declare_TCFType!(VTCompressionSession, VTCompressionSessionRef);
440 impl_TCFType!(
441 VTCompressionSession,
442 VTCompressionSessionRef,
443 VTCompressionSessionGetTypeID
444 );
445 impl_CFTypeDescription!(VTCompressionSession);
446
447 impl VTCompressionSession {
448 /// Create a new compression session.
449 ///
450 /// # Safety
451 ///
452 /// The callback must be a valid function pointer. and the callback_data must be valid
453 /// in whatever terms that callback expects.
454 pub unsafe fn new(
455 width: usize,
456 height: usize,
457 codec: CMVideoCodecType,
458 callback: VTCompressionOutputCallback,
459 callback_data: *const c_void,
460 ) -> Result<Self> {
461 let mut this = ptr::null();
462 let result = VTCompressionSessionCreate(
463 ptr::null(),
464 width as i32,
465 height as i32,
466 codec,
467 ptr::null(),
468 ptr::null(),
469 ptr::null(),
470 callback,
471 callback_data,
472 &mut this,
473 );
474
475 if result == 0 {
476 Ok(Self::wrap_under_create_rule(this))
477 } else {
478 Err(anyhow!(
479 "error creating compression session, code {}",
480 result
481 ))
482 }
483 }
484
485 /// # Safety
486 ///
487 /// The arguments to this function must be valid according to VTCompressionSessionEncodeFrame
488 pub unsafe fn encode_frame(
489 &self,
490 buffer: CVImageBufferRef,
491 presentation_timestamp: CMTime,
492 duration: CMTime,
493 ) -> Result<()> {
494 let result = VTCompressionSessionEncodeFrame(
495 self.as_concrete_TypeRef(),
496 buffer,
497 presentation_timestamp,
498 duration,
499 ptr::null(),
500 ptr::null(),
501 ptr::null_mut(),
502 );
503 if result == 0 {
504 Ok(())
505 } else {
506 Err(anyhow!("error encoding frame, code {}", result))
507 }
508 }
509 }
510
511 type VTCompressionOutputCallback = Option<
512 unsafe extern "C" fn(
513 outputCallbackRefCon: *mut c_void,
514 sourceFrameRefCon: *mut c_void,
515 status: OSStatus,
516 infoFlags: VTEncodeInfoFlags,
517 sampleBuffer: CMSampleBufferRef,
518 ),
519 >;
520
521 #[link(name = "VideoToolbox", kind = "framework")]
522 extern "C" {
523 fn VTCompressionSessionGetTypeID() -> CFTypeID;
524 fn VTCompressionSessionCreate(
525 allocator: CFAllocatorRef,
526 width: i32,
527 height: i32,
528 codec_type: CMVideoCodecType,
529 encoder_specification: CFDictionaryRef,
530 source_image_buffer_attributes: CFDictionaryRef,
531 compressed_data_allocator: CFAllocatorRef,
532 output_callback: VTCompressionOutputCallback,
533 output_callback_ref_con: *const c_void,
534 compression_session_out: *mut VTCompressionSessionRef,
535 ) -> OSStatus;
536 fn VTCompressionSessionEncodeFrame(
537 session: VTCompressionSessionRef,
538 image_buffer: CVImageBufferRef,
539 presentation_timestamp: CMTime,
540 duration: CMTime,
541 frame_properties: CFDictionaryRef,
542 source_frame_ref_con: *const c_void,
543 output_flags: *mut VTEncodeInfoFlags,
544 ) -> OSStatus;
545 }
546}