Skip to main content

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