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 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 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 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}