media.rs

  1#![allow(non_snake_case)]
  2#![allow(non_camel_case_types)]
  3
  4mod bindings;
  5
  6#[cfg(target_os = "macos")]
  7pub mod core_media {
  8    #![allow(non_snake_case)]
  9
 10    pub use crate::bindings::{
 11        CMItemIndex, CMSampleTimingInfo, CMTime, CMTimeMake, CMVideoCodecType,
 12        kCMSampleAttachmentKey_NotSync, kCMTimeInvalid, kCMVideoCodecType_H264,
 13    };
 14    use anyhow::{Result, anyhow};
 15    use core_foundation::{
 16        array::{CFArray, CFArrayRef},
 17        base::{CFTypeID, OSStatus, TCFType},
 18        declare_TCFType,
 19        dictionary::CFDictionary,
 20        impl_CFTypeDescription, impl_TCFType,
 21        string::CFString,
 22    };
 23    use core_video::image_buffer::{CVImageBuffer, CVImageBufferRef};
 24    use std::{ffi::c_void, ptr};
 25
 26    #[repr(C)]
 27    pub struct __CMSampleBuffer(c_void);
 28    // The ref type must be a pointer to the underlying struct.
 29    pub type CMSampleBufferRef = *const __CMSampleBuffer;
 30
 31    declare_TCFType!(CMSampleBuffer, CMSampleBufferRef);
 32    impl_TCFType!(CMSampleBuffer, CMSampleBufferRef, CMSampleBufferGetTypeID);
 33    impl_CFTypeDescription!(CMSampleBuffer);
 34
 35    impl CMSampleBuffer {
 36        pub fn attachments(&self) -> Vec<CFDictionary<CFString>> {
 37            unsafe {
 38                let attachments =
 39                    CMSampleBufferGetSampleAttachmentsArray(self.as_concrete_TypeRef(), true);
 40                CFArray::<CFDictionary>::wrap_under_get_rule(attachments)
 41                    .into_iter()
 42                    .map(|attachments| {
 43                        CFDictionary::wrap_under_get_rule(attachments.as_concrete_TypeRef())
 44                    })
 45                    .collect()
 46            }
 47        }
 48
 49        pub fn image_buffer(&self) -> Option<CVImageBuffer> {
 50            unsafe {
 51                let ptr = CMSampleBufferGetImageBuffer(self.as_concrete_TypeRef());
 52                if ptr.is_null() {
 53                    None
 54                } else {
 55                    Some(CVImageBuffer::wrap_under_get_rule(ptr))
 56                }
 57            }
 58        }
 59
 60        pub fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo> {
 61            unsafe {
 62                let mut timing_info = CMSampleTimingInfo {
 63                    duration: kCMTimeInvalid,
 64                    presentationTimeStamp: kCMTimeInvalid,
 65                    decodeTimeStamp: kCMTimeInvalid,
 66                };
 67                let result = CMSampleBufferGetSampleTimingInfo(
 68                    self.as_concrete_TypeRef(),
 69                    index as CMItemIndex,
 70                    &mut timing_info,
 71                );
 72
 73                if result == 0 {
 74                    Ok(timing_info)
 75                } else {
 76                    Err(anyhow!("error getting sample timing info, code {}", result))
 77                }
 78            }
 79        }
 80
 81        pub fn format_description(&self) -> CMFormatDescription {
 82            unsafe {
 83                CMFormatDescription::wrap_under_get_rule(CMSampleBufferGetFormatDescription(
 84                    self.as_concrete_TypeRef(),
 85                ))
 86            }
 87        }
 88
 89        pub fn data(&self) -> CMBlockBuffer {
 90            unsafe {
 91                CMBlockBuffer::wrap_under_get_rule(CMSampleBufferGetDataBuffer(
 92                    self.as_concrete_TypeRef(),
 93                ))
 94            }
 95        }
 96    }
 97
 98    #[link(name = "CoreMedia", kind = "framework")]
 99    unsafe extern "C" {
100        fn CMSampleBufferGetTypeID() -> CFTypeID;
101        fn CMSampleBufferGetSampleAttachmentsArray(
102            buffer: CMSampleBufferRef,
103            create_if_necessary: bool,
104        ) -> CFArrayRef;
105        fn CMSampleBufferGetImageBuffer(buffer: CMSampleBufferRef) -> CVImageBufferRef;
106        fn CMSampleBufferGetSampleTimingInfo(
107            buffer: CMSampleBufferRef,
108            index: CMItemIndex,
109            timing_info_out: *mut CMSampleTimingInfo,
110        ) -> OSStatus;
111        fn CMSampleBufferGetFormatDescription(buffer: CMSampleBufferRef) -> CMFormatDescriptionRef;
112        fn CMSampleBufferGetDataBuffer(sample_buffer: CMSampleBufferRef) -> CMBlockBufferRef;
113    }
114
115    #[repr(C)]
116    pub struct __CMFormatDescription(c_void);
117    pub type CMFormatDescriptionRef = *const __CMFormatDescription;
118
119    declare_TCFType!(CMFormatDescription, CMFormatDescriptionRef);
120    impl_TCFType!(
121        CMFormatDescription,
122        CMFormatDescriptionRef,
123        CMFormatDescriptionGetTypeID
124    );
125    impl_CFTypeDescription!(CMFormatDescription);
126
127    impl CMFormatDescription {
128        pub fn h264_parameter_set_count(&self) -> usize {
129            unsafe {
130                let mut count = 0;
131                let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
132                    self.as_concrete_TypeRef(),
133                    0,
134                    ptr::null_mut(),
135                    ptr::null_mut(),
136                    &mut count,
137                    ptr::null_mut(),
138                );
139                assert_eq!(result, 0);
140                count
141            }
142        }
143
144        pub fn h264_parameter_set_at_index(&self, index: usize) -> Result<&[u8]> {
145            unsafe {
146                let mut bytes = ptr::null();
147                let mut len = 0;
148                let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
149                    self.as_concrete_TypeRef(),
150                    index,
151                    &mut bytes,
152                    &mut len,
153                    ptr::null_mut(),
154                    ptr::null_mut(),
155                );
156                if result == 0 {
157                    Ok(std::slice::from_raw_parts(bytes, len))
158                } else {
159                    Err(anyhow!("error getting parameter set, code: {}", result))
160                }
161            }
162        }
163    }
164
165    #[link(name = "CoreMedia", kind = "framework")]
166    unsafe extern "C" {
167        fn CMFormatDescriptionGetTypeID() -> CFTypeID;
168        fn CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
169            video_desc: CMFormatDescriptionRef,
170            parameter_set_index: usize,
171            parameter_set_pointer_out: *mut *const u8,
172            parameter_set_size_out: *mut usize,
173            parameter_set_count_out: *mut usize,
174            NALUnitHeaderLengthOut: *mut isize,
175        ) -> OSStatus;
176    }
177
178    #[repr(C)]
179    pub struct __CMBlockBuffer(c_void);
180    pub type CMBlockBufferRef = *const __CMBlockBuffer;
181
182    declare_TCFType!(CMBlockBuffer, CMBlockBufferRef);
183    impl_TCFType!(CMBlockBuffer, CMBlockBufferRef, CMBlockBufferGetTypeID);
184    impl_CFTypeDescription!(CMBlockBuffer);
185
186    impl CMBlockBuffer {
187        pub fn bytes(&self) -> &[u8] {
188            unsafe {
189                let mut bytes = ptr::null();
190                let mut len = 0;
191                let result = CMBlockBufferGetDataPointer(
192                    self.as_concrete_TypeRef(),
193                    0,
194                    &mut 0,
195                    &mut len,
196                    &mut bytes,
197                );
198                assert!(result == 0, "could not get block buffer data");
199                std::slice::from_raw_parts(bytes, len)
200            }
201        }
202    }
203
204    #[link(name = "CoreMedia", kind = "framework")]
205    unsafe extern "C" {
206        fn CMBlockBufferGetTypeID() -> CFTypeID;
207        fn CMBlockBufferGetDataPointer(
208            buffer: CMBlockBufferRef,
209            offset: usize,
210            length_at_offset_out: *mut usize,
211            total_length_out: *mut usize,
212            data_pointer_out: *mut *const u8,
213        ) -> OSStatus;
214    }
215}
216
217#[cfg(target_os = "macos")]
218pub mod core_video {
219    #![allow(non_snake_case)]
220
221    #[cfg(target_os = "macos")]
222    use core_foundation::{
223        base::{CFTypeID, TCFType},
224        declare_TCFType, impl_CFTypeDescription, impl_TCFType,
225    };
226    #[cfg(target_os = "macos")]
227    use std::ffi::c_void;
228
229    use crate::bindings::{CVReturn, kCVReturnSuccess};
230    pub use crate::bindings::{
231        kCVPixelFormatType_32BGRA, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
232        kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, kCVPixelFormatType_420YpCbCr8Planar,
233    };
234    use anyhow::{Result, anyhow};
235    use core_foundation::{
236        base::kCFAllocatorDefault, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef,
237    };
238    use foreign_types::ForeignTypeRef;
239
240    use metal::{MTLDevice, MTLPixelFormat};
241    use std::ptr;
242
243    #[repr(C)]
244    pub struct __CVMetalTextureCache(c_void);
245    pub type CVMetalTextureCacheRef = *const __CVMetalTextureCache;
246
247    declare_TCFType!(CVMetalTextureCache, CVMetalTextureCacheRef);
248    impl_TCFType!(
249        CVMetalTextureCache,
250        CVMetalTextureCacheRef,
251        CVMetalTextureCacheGetTypeID
252    );
253    impl_CFTypeDescription!(CVMetalTextureCache);
254
255    impl CVMetalTextureCache {
256        /// # Safety
257        ///
258        /// metal_device must be valid according to CVMetalTextureCacheCreate
259        pub unsafe fn new(metal_device: *mut MTLDevice) -> Result<Self> {
260            let mut this = ptr::null();
261            let result = unsafe {
262                CVMetalTextureCacheCreate(
263                    kCFAllocatorDefault,
264                    ptr::null(),
265                    metal_device,
266                    ptr::null(),
267                    &mut this,
268                )
269            };
270            if result == kCVReturnSuccess {
271                unsafe { Ok(CVMetalTextureCache::wrap_under_create_rule(this)) }
272            } else {
273                Err(anyhow!("could not create texture cache, code: {}", result))
274            }
275        }
276
277        /// # Safety
278        ///
279        /// The arguments to this function must be valid according to CVMetalTextureCacheCreateTextureFromImage
280        pub unsafe fn create_texture_from_image(
281            &self,
282            source: ::core_video::image_buffer::CVImageBufferRef,
283            texture_attributes: CFDictionaryRef,
284            pixel_format: MTLPixelFormat,
285            width: usize,
286            height: usize,
287            plane_index: usize,
288        ) -> Result<CVMetalTexture> {
289            let mut this = ptr::null();
290            let result = unsafe {
291                CVMetalTextureCacheCreateTextureFromImage(
292                    kCFAllocatorDefault,
293                    self.as_concrete_TypeRef(),
294                    source,
295                    texture_attributes,
296                    pixel_format,
297                    width,
298                    height,
299                    plane_index,
300                    &mut this,
301                )
302            };
303            if result == kCVReturnSuccess {
304                unsafe { Ok(CVMetalTexture::wrap_under_create_rule(this)) }
305            } else {
306                Err(anyhow!("could not create texture, code: {}", result))
307            }
308        }
309    }
310
311    #[link(name = "CoreVideo", kind = "framework")]
312    unsafe extern "C" {
313        fn CVMetalTextureCacheGetTypeID() -> CFTypeID;
314        fn CVMetalTextureCacheCreate(
315            allocator: CFAllocatorRef,
316            cache_attributes: CFDictionaryRef,
317            metal_device: *const MTLDevice,
318            texture_attributes: CFDictionaryRef,
319            cache_out: *mut CVMetalTextureCacheRef,
320        ) -> CVReturn;
321        fn CVMetalTextureCacheCreateTextureFromImage(
322            allocator: CFAllocatorRef,
323            texture_cache: CVMetalTextureCacheRef,
324            source_image: ::core_video::image_buffer::CVImageBufferRef,
325            texture_attributes: CFDictionaryRef,
326            pixel_format: MTLPixelFormat,
327            width: usize,
328            height: usize,
329            plane_index: usize,
330            texture_out: *mut CVMetalTextureRef,
331        ) -> CVReturn;
332    }
333
334    #[repr(C)]
335    pub struct __CVMetalTexture(c_void);
336    pub type CVMetalTextureRef = *const __CVMetalTexture;
337
338    declare_TCFType!(CVMetalTexture, CVMetalTextureRef);
339    impl_TCFType!(CVMetalTexture, CVMetalTextureRef, CVMetalTextureGetTypeID);
340    impl_CFTypeDescription!(CVMetalTexture);
341
342    impl CVMetalTexture {
343        pub fn as_texture_ref(&self) -> &metal::TextureRef {
344            unsafe {
345                let texture = CVMetalTextureGetTexture(self.as_concrete_TypeRef());
346                metal::TextureRef::from_ptr(texture as *mut _)
347            }
348        }
349    }
350
351    #[link(name = "CoreVideo", kind = "framework")]
352    unsafe extern "C" {
353        fn CVMetalTextureGetTypeID() -> CFTypeID;
354        fn CVMetalTextureGetTexture(texture: CVMetalTextureRef) -> *mut c_void;
355    }
356}