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) -> Option<CVImageBuffer> {
257 unsafe {
258 let ptr = CMSampleBufferGetImageBuffer(self.as_concrete_TypeRef());
259 if ptr.is_null() {
260 None
261 } else {
262 Some(CVImageBuffer::wrap_under_get_rule(ptr))
263 }
264 }
265 }
266
267 pub fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo> {
268 unsafe {
269 let mut timing_info = CMSampleTimingInfo {
270 duration: kCMTimeInvalid,
271 presentationTimeStamp: kCMTimeInvalid,
272 decodeTimeStamp: kCMTimeInvalid,
273 };
274 let result = CMSampleBufferGetSampleTimingInfo(
275 self.as_concrete_TypeRef(),
276 index as CMItemIndex,
277 &mut timing_info,
278 );
279
280 if result == 0 {
281 Ok(timing_info)
282 } else {
283 Err(anyhow!("error getting sample timing info, code {}", result))
284 }
285 }
286 }
287
288 pub fn format_description(&self) -> CMFormatDescription {
289 unsafe {
290 CMFormatDescription::wrap_under_get_rule(CMSampleBufferGetFormatDescription(
291 self.as_concrete_TypeRef(),
292 ))
293 }
294 }
295
296 pub fn data(&self) -> CMBlockBuffer {
297 unsafe {
298 CMBlockBuffer::wrap_under_get_rule(CMSampleBufferGetDataBuffer(
299 self.as_concrete_TypeRef(),
300 ))
301 }
302 }
303 }
304
305 #[link(name = "CoreMedia", kind = "framework")]
306 extern "C" {
307 fn CMSampleBufferGetTypeID() -> CFTypeID;
308 fn CMSampleBufferGetSampleAttachmentsArray(
309 buffer: CMSampleBufferRef,
310 create_if_necessary: bool,
311 ) -> CFArrayRef;
312 fn CMSampleBufferGetImageBuffer(buffer: CMSampleBufferRef) -> CVImageBufferRef;
313 fn CMSampleBufferGetSampleTimingInfo(
314 buffer: CMSampleBufferRef,
315 index: CMItemIndex,
316 timing_info_out: *mut CMSampleTimingInfo,
317 ) -> OSStatus;
318 fn CMSampleBufferGetFormatDescription(buffer: CMSampleBufferRef) -> CMFormatDescriptionRef;
319 fn CMSampleBufferGetDataBuffer(sample_buffer: CMSampleBufferRef) -> CMBlockBufferRef;
320 }
321
322 #[repr(C)]
323 pub struct __CMFormatDescription(c_void);
324 pub type CMFormatDescriptionRef = *const __CMFormatDescription;
325
326 declare_TCFType!(CMFormatDescription, CMFormatDescriptionRef);
327 impl_TCFType!(
328 CMFormatDescription,
329 CMFormatDescriptionRef,
330 CMFormatDescriptionGetTypeID
331 );
332 impl_CFTypeDescription!(CMFormatDescription);
333
334 impl CMFormatDescription {
335 pub fn h264_parameter_set_count(&self) -> usize {
336 unsafe {
337 let mut count = 0;
338 let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
339 self.as_concrete_TypeRef(),
340 0,
341 ptr::null_mut(),
342 ptr::null_mut(),
343 &mut count,
344 ptr::null_mut(),
345 );
346 assert_eq!(result, 0);
347 count
348 }
349 }
350
351 pub fn h264_parameter_set_at_index(&self, index: usize) -> Result<&[u8]> {
352 unsafe {
353 let mut bytes = ptr::null();
354 let mut len = 0;
355 let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
356 self.as_concrete_TypeRef(),
357 index,
358 &mut bytes,
359 &mut len,
360 ptr::null_mut(),
361 ptr::null_mut(),
362 );
363 if result == 0 {
364 Ok(std::slice::from_raw_parts(bytes, len))
365 } else {
366 Err(anyhow!("error getting parameter set, code: {}", result))
367 }
368 }
369 }
370 }
371
372 #[link(name = "CoreMedia", kind = "framework")]
373 extern "C" {
374 fn CMFormatDescriptionGetTypeID() -> CFTypeID;
375 fn CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
376 video_desc: CMFormatDescriptionRef,
377 parameter_set_index: usize,
378 parameter_set_pointer_out: *mut *const u8,
379 parameter_set_size_out: *mut usize,
380 parameter_set_count_out: *mut usize,
381 NALUnitHeaderLengthOut: *mut isize,
382 ) -> OSStatus;
383 }
384
385 #[repr(C)]
386 pub struct __CMBlockBuffer(c_void);
387 pub type CMBlockBufferRef = *const __CMBlockBuffer;
388
389 declare_TCFType!(CMBlockBuffer, CMBlockBufferRef);
390 impl_TCFType!(CMBlockBuffer, CMBlockBufferRef, CMBlockBufferGetTypeID);
391 impl_CFTypeDescription!(CMBlockBuffer);
392
393 impl CMBlockBuffer {
394 pub fn bytes(&self) -> &[u8] {
395 unsafe {
396 let mut bytes = ptr::null();
397 let mut len = 0;
398 let result = CMBlockBufferGetDataPointer(
399 self.as_concrete_TypeRef(),
400 0,
401 &mut 0,
402 &mut len,
403 &mut bytes,
404 );
405 assert!(result == 0, "could not get block buffer data");
406 std::slice::from_raw_parts(bytes, len)
407 }
408 }
409 }
410
411 #[link(name = "CoreMedia", kind = "framework")]
412 extern "C" {
413 fn CMBlockBufferGetTypeID() -> CFTypeID;
414 fn CMBlockBufferGetDataPointer(
415 buffer: CMBlockBufferRef,
416 offset: usize,
417 length_at_offset_out: *mut usize,
418 total_length_out: *mut usize,
419 data_pointer_out: *mut *const u8,
420 ) -> OSStatus;
421 }
422}
423
424#[cfg(target_os = "macos")]
425pub mod video_toolbox {
426 #![allow(non_snake_case)]
427
428 use super::*;
429 use crate::{
430 core_media::{CMSampleBufferRef, CMTime, CMVideoCodecType},
431 core_video::CVImageBufferRef,
432 };
433 use anyhow::{anyhow, Result};
434 pub use bindings::VTEncodeInfoFlags;
435 use core_foundation::{base::OSStatus, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef};
436 use std::ptr;
437
438 #[repr(C)]
439 pub struct __VTCompressionSession(c_void);
440 // The ref type must be a pointer to the underlying struct.
441 pub type VTCompressionSessionRef = *const __VTCompressionSession;
442
443 declare_TCFType!(VTCompressionSession, VTCompressionSessionRef);
444 impl_TCFType!(
445 VTCompressionSession,
446 VTCompressionSessionRef,
447 VTCompressionSessionGetTypeID
448 );
449 impl_CFTypeDescription!(VTCompressionSession);
450
451 impl VTCompressionSession {
452 /// Create a new compression session.
453 ///
454 /// # Safety
455 ///
456 /// The callback must be a valid function pointer. and the callback_data must be valid
457 /// in whatever terms that callback expects.
458 pub unsafe fn new(
459 width: usize,
460 height: usize,
461 codec: CMVideoCodecType,
462 callback: VTCompressionOutputCallback,
463 callback_data: *const c_void,
464 ) -> Result<Self> {
465 let mut this = ptr::null();
466 let result = VTCompressionSessionCreate(
467 ptr::null(),
468 width as i32,
469 height as i32,
470 codec,
471 ptr::null(),
472 ptr::null(),
473 ptr::null(),
474 callback,
475 callback_data,
476 &mut this,
477 );
478
479 if result == 0 {
480 Ok(Self::wrap_under_create_rule(this))
481 } else {
482 Err(anyhow!(
483 "error creating compression session, code {}",
484 result
485 ))
486 }
487 }
488
489 /// # Safety
490 ///
491 /// The arguments to this function must be valid according to VTCompressionSessionEncodeFrame
492 pub unsafe fn encode_frame(
493 &self,
494 buffer: CVImageBufferRef,
495 presentation_timestamp: CMTime,
496 duration: CMTime,
497 ) -> Result<()> {
498 let result = VTCompressionSessionEncodeFrame(
499 self.as_concrete_TypeRef(),
500 buffer,
501 presentation_timestamp,
502 duration,
503 ptr::null(),
504 ptr::null(),
505 ptr::null_mut(),
506 );
507 if result == 0 {
508 Ok(())
509 } else {
510 Err(anyhow!("error encoding frame, code {}", result))
511 }
512 }
513 }
514
515 type VTCompressionOutputCallback = Option<
516 unsafe extern "C" fn(
517 outputCallbackRefCon: *mut c_void,
518 sourceFrameRefCon: *mut c_void,
519 status: OSStatus,
520 infoFlags: VTEncodeInfoFlags,
521 sampleBuffer: CMSampleBufferRef,
522 ),
523 >;
524
525 #[link(name = "VideoToolbox", kind = "framework")]
526 extern "C" {
527 fn VTCompressionSessionGetTypeID() -> CFTypeID;
528 fn VTCompressionSessionCreate(
529 allocator: CFAllocatorRef,
530 width: i32,
531 height: i32,
532 codec_type: CMVideoCodecType,
533 encoder_specification: CFDictionaryRef,
534 source_image_buffer_attributes: CFDictionaryRef,
535 compressed_data_allocator: CFAllocatorRef,
536 output_callback: VTCompressionOutputCallback,
537 output_callback_ref_con: *const c_void,
538 compression_session_out: *mut VTCompressionSessionRef,
539 ) -> OSStatus;
540 fn VTCompressionSessionEncodeFrame(
541 session: VTCompressionSessionRef,
542 image_buffer: CVImageBufferRef,
543 presentation_timestamp: CMTime,
544 duration: CMTime,
545 frame_properties: CFDictionaryRef,
546 source_frame_ref_con: *const c_void,
547 output_flags: *mut VTEncodeInfoFlags,
548 ) -> OSStatus;
549 }
550}