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