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