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) -> Option<CVImageBuffer> {
257            unsafe {
258                let ptr = CMSampleBufferGetImageBuffer(self.as_concrete_TypeRef());
259                if ptr.is_null() {
260                    None
261                } else {
262                    Some(CVImageBuffer::wrap_under_get_rule(ptr))
263                }
264            }
265        }
266
267        pub fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo> {
268            unsafe {
269                let mut timing_info = CMSampleTimingInfo {
270                    duration: kCMTimeInvalid,
271                    presentationTimeStamp: kCMTimeInvalid,
272                    decodeTimeStamp: kCMTimeInvalid,
273                };
274                let result = CMSampleBufferGetSampleTimingInfo(
275                    self.as_concrete_TypeRef(),
276                    index as CMItemIndex,
277                    &mut timing_info,
278                );
279
280                if result == 0 {
281                    Ok(timing_info)
282                } else {
283                    Err(anyhow!("error getting sample timing info, code {}", result))
284                }
285            }
286        }
287
288        pub fn format_description(&self) -> CMFormatDescription {
289            unsafe {
290                CMFormatDescription::wrap_under_get_rule(CMSampleBufferGetFormatDescription(
291                    self.as_concrete_TypeRef(),
292                ))
293            }
294        }
295
296        pub fn data(&self) -> CMBlockBuffer {
297            unsafe {
298                CMBlockBuffer::wrap_under_get_rule(CMSampleBufferGetDataBuffer(
299                    self.as_concrete_TypeRef(),
300                ))
301            }
302        }
303    }
304
305    #[link(name = "CoreMedia", kind = "framework")]
306    extern "C" {
307        fn CMSampleBufferGetTypeID() -> CFTypeID;
308        fn CMSampleBufferGetSampleAttachmentsArray(
309            buffer: CMSampleBufferRef,
310            create_if_necessary: bool,
311        ) -> CFArrayRef;
312        fn CMSampleBufferGetImageBuffer(buffer: CMSampleBufferRef) -> CVImageBufferRef;
313        fn CMSampleBufferGetSampleTimingInfo(
314            buffer: CMSampleBufferRef,
315            index: CMItemIndex,
316            timing_info_out: *mut CMSampleTimingInfo,
317        ) -> OSStatus;
318        fn CMSampleBufferGetFormatDescription(buffer: CMSampleBufferRef) -> CMFormatDescriptionRef;
319        fn CMSampleBufferGetDataBuffer(sample_buffer: CMSampleBufferRef) -> CMBlockBufferRef;
320    }
321
322    #[repr(C)]
323    pub struct __CMFormatDescription(c_void);
324    pub type CMFormatDescriptionRef = *const __CMFormatDescription;
325
326    declare_TCFType!(CMFormatDescription, CMFormatDescriptionRef);
327    impl_TCFType!(
328        CMFormatDescription,
329        CMFormatDescriptionRef,
330        CMFormatDescriptionGetTypeID
331    );
332    impl_CFTypeDescription!(CMFormatDescription);
333
334    impl CMFormatDescription {
335        pub fn h264_parameter_set_count(&self) -> usize {
336            unsafe {
337                let mut count = 0;
338                let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
339                    self.as_concrete_TypeRef(),
340                    0,
341                    ptr::null_mut(),
342                    ptr::null_mut(),
343                    &mut count,
344                    ptr::null_mut(),
345                );
346                assert_eq!(result, 0);
347                count
348            }
349        }
350
351        pub fn h264_parameter_set_at_index(&self, index: usize) -> Result<&[u8]> {
352            unsafe {
353                let mut bytes = ptr::null();
354                let mut len = 0;
355                let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
356                    self.as_concrete_TypeRef(),
357                    index,
358                    &mut bytes,
359                    &mut len,
360                    ptr::null_mut(),
361                    ptr::null_mut(),
362                );
363                if result == 0 {
364                    Ok(std::slice::from_raw_parts(bytes, len))
365                } else {
366                    Err(anyhow!("error getting parameter set, code: {}", result))
367                }
368            }
369        }
370    }
371
372    #[link(name = "CoreMedia", kind = "framework")]
373    extern "C" {
374        fn CMFormatDescriptionGetTypeID() -> CFTypeID;
375        fn CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
376            video_desc: CMFormatDescriptionRef,
377            parameter_set_index: usize,
378            parameter_set_pointer_out: *mut *const u8,
379            parameter_set_size_out: *mut usize,
380            parameter_set_count_out: *mut usize,
381            NALUnitHeaderLengthOut: *mut isize,
382        ) -> OSStatus;
383    }
384
385    #[repr(C)]
386    pub struct __CMBlockBuffer(c_void);
387    pub type CMBlockBufferRef = *const __CMBlockBuffer;
388
389    declare_TCFType!(CMBlockBuffer, CMBlockBufferRef);
390    impl_TCFType!(CMBlockBuffer, CMBlockBufferRef, CMBlockBufferGetTypeID);
391    impl_CFTypeDescription!(CMBlockBuffer);
392
393    impl CMBlockBuffer {
394        pub fn bytes(&self) -> &[u8] {
395            unsafe {
396                let mut bytes = ptr::null();
397                let mut len = 0;
398                let result = CMBlockBufferGetDataPointer(
399                    self.as_concrete_TypeRef(),
400                    0,
401                    &mut 0,
402                    &mut len,
403                    &mut bytes,
404                );
405                assert!(result == 0, "could not get block buffer data");
406                std::slice::from_raw_parts(bytes, len)
407            }
408        }
409    }
410
411    #[link(name = "CoreMedia", kind = "framework")]
412    extern "C" {
413        fn CMBlockBufferGetTypeID() -> CFTypeID;
414        fn CMBlockBufferGetDataPointer(
415            buffer: CMBlockBufferRef,
416            offset: usize,
417            length_at_offset_out: *mut usize,
418            total_length_out: *mut usize,
419            data_pointer_out: *mut *const u8,
420        ) -> OSStatus;
421    }
422}
423
424#[cfg(target_os = "macos")]
425pub mod video_toolbox {
426    #![allow(non_snake_case)]
427
428    use super::*;
429    use crate::{
430        core_media::{CMSampleBufferRef, CMTime, CMVideoCodecType},
431        core_video::CVImageBufferRef,
432    };
433    use anyhow::{anyhow, Result};
434    pub use bindings::VTEncodeInfoFlags;
435    use core_foundation::{base::OSStatus, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef};
436    use std::ptr;
437
438    #[repr(C)]
439    pub struct __VTCompressionSession(c_void);
440    // The ref type must be a pointer to the underlying struct.
441    pub type VTCompressionSessionRef = *const __VTCompressionSession;
442
443    declare_TCFType!(VTCompressionSession, VTCompressionSessionRef);
444    impl_TCFType!(
445        VTCompressionSession,
446        VTCompressionSessionRef,
447        VTCompressionSessionGetTypeID
448    );
449    impl_CFTypeDescription!(VTCompressionSession);
450
451    impl VTCompressionSession {
452        /// Create a new compression session.
453        ///
454        /// # Safety
455        ///
456        /// The callback must be a valid function pointer. and the callback_data must be valid
457        /// in whatever terms that callback expects.
458        pub unsafe fn new(
459            width: usize,
460            height: usize,
461            codec: CMVideoCodecType,
462            callback: VTCompressionOutputCallback,
463            callback_data: *const c_void,
464        ) -> Result<Self> {
465            let mut this = ptr::null();
466            let result = VTCompressionSessionCreate(
467                ptr::null(),
468                width as i32,
469                height as i32,
470                codec,
471                ptr::null(),
472                ptr::null(),
473                ptr::null(),
474                callback,
475                callback_data,
476                &mut this,
477            );
478
479            if result == 0 {
480                Ok(Self::wrap_under_create_rule(this))
481            } else {
482                Err(anyhow!(
483                    "error creating compression session, code {}",
484                    result
485                ))
486            }
487        }
488
489        /// # Safety
490        ///
491        /// The arguments to this function must be valid according to VTCompressionSessionEncodeFrame
492        pub unsafe fn encode_frame(
493            &self,
494            buffer: CVImageBufferRef,
495            presentation_timestamp: CMTime,
496            duration: CMTime,
497        ) -> Result<()> {
498            let result = VTCompressionSessionEncodeFrame(
499                self.as_concrete_TypeRef(),
500                buffer,
501                presentation_timestamp,
502                duration,
503                ptr::null(),
504                ptr::null(),
505                ptr::null_mut(),
506            );
507            if result == 0 {
508                Ok(())
509            } else {
510                Err(anyhow!("error encoding frame, code {}", result))
511            }
512        }
513    }
514
515    type VTCompressionOutputCallback = Option<
516        unsafe extern "C" fn(
517            outputCallbackRefCon: *mut c_void,
518            sourceFrameRefCon: *mut c_void,
519            status: OSStatus,
520            infoFlags: VTEncodeInfoFlags,
521            sampleBuffer: CMSampleBufferRef,
522        ),
523    >;
524
525    #[link(name = "VideoToolbox", kind = "framework")]
526    extern "C" {
527        fn VTCompressionSessionGetTypeID() -> CFTypeID;
528        fn VTCompressionSessionCreate(
529            allocator: CFAllocatorRef,
530            width: i32,
531            height: i32,
532            codec_type: CMVideoCodecType,
533            encoder_specification: CFDictionaryRef,
534            source_image_buffer_attributes: CFDictionaryRef,
535            compressed_data_allocator: CFAllocatorRef,
536            output_callback: VTCompressionOutputCallback,
537            output_callback_ref_con: *const c_void,
538            compression_session_out: *mut VTCompressionSessionRef,
539        ) -> OSStatus;
540        fn VTCompressionSessionEncodeFrame(
541            session: VTCompressionSessionRef,
542            image_buffer: CVImageBufferRef,
543            presentation_timestamp: CMTime,
544            duration: CMTime,
545            frame_properties: CFDictionaryRef,
546            source_frame_ref_con: *const c_void,
547            output_flags: *mut VTEncodeInfoFlags,
548        ) -> OSStatus;
549    }
550}