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 kCMSampleAttachmentKey_NotSync, kCMTimeInvalid, kCMVideoCodecType_H264, CMItemIndex,
12 CMSampleTimingInfo, CMTime, CMTimeMake, CMVideoCodecType,
13 };
14 use anyhow::{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
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 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 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 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 pub use crate::bindings::{
230 kCVPixelFormatType_32BGRA, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
231 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, kCVPixelFormatType_420YpCbCr8Planar,
232 };
233 use crate::bindings::{kCVReturnSuccess, CVReturn};
234 use anyhow::{anyhow, Result};
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 = CVMetalTextureCacheCreate(
262 kCFAllocatorDefault,
263 ptr::null(),
264 metal_device,
265 ptr::null(),
266 &mut this,
267 );
268 if result == kCVReturnSuccess {
269 Ok(CVMetalTextureCache::wrap_under_create_rule(this))
270 } else {
271 Err(anyhow!("could not create texture cache, code: {}", result))
272 }
273 }
274
275 /// # Safety
276 ///
277 /// The arguments to this function must be valid according to CVMetalTextureCacheCreateTextureFromImage
278 pub unsafe fn create_texture_from_image(
279 &self,
280 source: ::core_video::image_buffer::CVImageBufferRef,
281 texture_attributes: CFDictionaryRef,
282 pixel_format: MTLPixelFormat,
283 width: usize,
284 height: usize,
285 plane_index: usize,
286 ) -> Result<CVMetalTexture> {
287 let mut this = ptr::null();
288 let result = CVMetalTextureCacheCreateTextureFromImage(
289 kCFAllocatorDefault,
290 self.as_concrete_TypeRef(),
291 source,
292 texture_attributes,
293 pixel_format,
294 width,
295 height,
296 plane_index,
297 &mut this,
298 );
299 if result == kCVReturnSuccess {
300 Ok(CVMetalTexture::wrap_under_create_rule(this))
301 } else {
302 Err(anyhow!("could not create texture, code: {}", result))
303 }
304 }
305 }
306
307 #[link(name = "CoreVideo", kind = "framework")]
308 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 extern "C" {
349 fn CVMetalTextureGetTypeID() -> CFTypeID;
350 fn CVMetalTextureGetTexture(texture: CVMetalTextureRef) -> *mut c_void;
351 }
352}