media.rs

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