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        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}