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