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