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