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