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