media.rs

  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}