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