1#![allow(
2 clippy::cast_possible_wrap,
3 clippy::missing_const_for_fn,
4 clippy::missing_errors_doc,
5 clippy::too_many_arguments
6)]
7
8use core::ffi::c_void;
14
15use apple_cf::{cm::CMTime, cv::CVPixelBuffer};
16use apple_metal::CommandBuffer;
17
18use crate::{ffi, VTError};
19
20extern "C" {
21 fn vt_super_resolution_is_supported() -> bool;
22 fn vt_motion_blur_is_supported() -> bool;
23 fn vt_temporal_noise_filter_is_supported() -> bool;
24 fn vt_frame_rate_conversion_is_supported() -> bool;
25 fn vt_low_latency_super_resolution_is_supported() -> bool;
26 fn vt_low_latency_frame_interpolation_is_supported() -> bool;
27 fn vt_optical_flow_is_supported() -> bool;
28
29 fn vt_super_resolution_supported_scale_factors(out_buf: *mut u32, max: usize) -> usize;
30 fn vt_low_latency_super_resolution_supported_scale_factors(
31 frame_width: isize,
32 frame_height: isize,
33 out_buf: *mut f32,
34 max: usize,
35 ) -> usize;
36
37 fn vt_super_resolution_model_status(
38 frame_width: isize,
39 frame_height: isize,
40 scale_factor: isize,
41 use_precomputed_flow: bool,
42 input_type: isize,
43 quality_prioritization: isize,
44 revision: isize,
45 ) -> i32;
46 fn vt_super_resolution_model_percentage_available(
47 frame_width: isize,
48 frame_height: isize,
49 scale_factor: isize,
50 use_precomputed_flow: bool,
51 input_type: isize,
52 quality_prioritization: isize,
53 revision: isize,
54 ) -> f32;
55 fn vt_super_resolution_download_model(
56 frame_width: isize,
57 frame_height: isize,
58 scale_factor: isize,
59 use_precomputed_flow: bool,
60 input_type: isize,
61 quality_prioritization: isize,
62 revision: isize,
63 ) -> i32;
64
65 fn vt_super_resolution_start(
66 frame_width: isize,
67 frame_height: isize,
68 scale_factor: isize,
69 use_precomputed_flow: bool,
70 input_type: isize,
71 quality_prioritization: isize,
72 revision: isize,
73 out: *mut *mut c_void,
74 ) -> i32;
75 fn vt_motion_blur_start(
76 frame_width: isize,
77 frame_height: isize,
78 use_precomputed_flow: bool,
79 quality_prioritization: isize,
80 revision: isize,
81 out: *mut *mut c_void,
82 ) -> i32;
83 fn vt_temporal_noise_filter_start(
84 frame_width: isize,
85 frame_height: isize,
86 source_pixel_format: u32,
87 out: *mut *mut c_void,
88 ) -> i32;
89 fn vt_frame_rate_conversion_start(
90 frame_width: isize,
91 frame_height: isize,
92 use_precomputed_flow: bool,
93 quality_prioritization: isize,
94 revision: isize,
95 out: *mut *mut c_void,
96 ) -> i32;
97 fn vt_low_latency_super_resolution_start(
98 frame_width: isize,
99 frame_height: isize,
100 scale_factor: f32,
101 out: *mut *mut c_void,
102 ) -> i32;
103 fn vt_low_latency_frame_interpolation_start(
104 frame_width: isize,
105 frame_height: isize,
106 number_of_interpolated_frames: isize,
107 out: *mut *mut c_void,
108 ) -> i32;
109 fn vt_optical_flow_start(
110 frame_width: isize,
111 frame_height: isize,
112 quality_prioritization: isize,
113 revision: isize,
114 out: *mut *mut c_void,
115 ) -> i32;
116
117 fn vt_frame_processor_frame_create(
118 buffer: *mut c_void,
119 presentation_time_stamp: ffi::CMTime,
120 out: *mut *mut c_void,
121 ) -> i32;
122 fn vt_frame_processor_frame_release(frame: *mut c_void);
123
124 fn vt_frame_processor_optical_flow_create(
125 forward_flow: *mut c_void,
126 backward_flow: *mut c_void,
127 out: *mut *mut c_void,
128 ) -> i32;
129 fn vt_frame_processor_optical_flow_release(flow: *mut c_void);
130
131 fn vt_frame_processor_process_super_resolution(
132 processor: *mut c_void,
133 source_frame: *mut c_void,
134 previous_frame: *mut c_void,
135 previous_output_frame: *mut c_void,
136 optical_flow: *mut c_void,
137 submission_mode: i32,
138 destination_frame: *mut c_void,
139 ) -> i32;
140 fn vt_frame_processor_process_super_resolution_with_command_buffer(
141 processor: *mut c_void,
142 command_buffer: *mut c_void,
143 source_frame: *mut c_void,
144 previous_frame: *mut c_void,
145 previous_output_frame: *mut c_void,
146 optical_flow: *mut c_void,
147 submission_mode: i32,
148 destination_frame: *mut c_void,
149 ) -> i32;
150 fn vt_frame_processor_process_motion_blur(
151 processor: *mut c_void,
152 source_frame: *mut c_void,
153 next_frame: *mut c_void,
154 previous_frame: *mut c_void,
155 next_optical_flow: *mut c_void,
156 previous_optical_flow: *mut c_void,
157 motion_blur_strength: isize,
158 submission_mode: i32,
159 destination_frame: *mut c_void,
160 ) -> i32;
161 fn vt_frame_processor_process_motion_blur_with_command_buffer(
162 processor: *mut c_void,
163 command_buffer: *mut c_void,
164 source_frame: *mut c_void,
165 next_frame: *mut c_void,
166 previous_frame: *mut c_void,
167 next_optical_flow: *mut c_void,
168 previous_optical_flow: *mut c_void,
169 motion_blur_strength: isize,
170 submission_mode: i32,
171 destination_frame: *mut c_void,
172 ) -> i32;
173 fn vt_frame_processor_process_temporal_noise_filter(
174 processor: *mut c_void,
175 source_frame: *mut c_void,
176 next_frames: *const *mut c_void,
177 next_frame_count: usize,
178 previous_frames: *const *mut c_void,
179 previous_frame_count: usize,
180 destination_frame: *mut c_void,
181 filter_strength: f32,
182 has_discontinuity: bool,
183 ) -> i32;
184 fn vt_frame_processor_process_temporal_noise_filter_with_command_buffer(
185 processor: *mut c_void,
186 command_buffer: *mut c_void,
187 source_frame: *mut c_void,
188 next_frames: *const *mut c_void,
189 next_frame_count: usize,
190 previous_frames: *const *mut c_void,
191 previous_frame_count: usize,
192 destination_frame: *mut c_void,
193 filter_strength: f32,
194 has_discontinuity: bool,
195 ) -> i32;
196 fn vt_frame_processor_process_frame_rate_conversion(
197 processor: *mut c_void,
198 source_frame: *mut c_void,
199 next_frame: *mut c_void,
200 optical_flow: *mut c_void,
201 interpolation_phase: *const f32,
202 interpolation_phase_count: usize,
203 submission_mode: i32,
204 destination_frames: *const *mut c_void,
205 destination_frame_count: usize,
206 ) -> i32;
207 fn vt_frame_processor_process_frame_rate_conversion_with_command_buffer(
208 processor: *mut c_void,
209 command_buffer: *mut c_void,
210 source_frame: *mut c_void,
211 next_frame: *mut c_void,
212 optical_flow: *mut c_void,
213 interpolation_phase: *const f32,
214 interpolation_phase_count: usize,
215 submission_mode: i32,
216 destination_frames: *const *mut c_void,
217 destination_frame_count: usize,
218 ) -> i32;
219 fn vt_frame_processor_process_low_latency_super_resolution(
220 processor: *mut c_void,
221 source_frame: *mut c_void,
222 destination_frame: *mut c_void,
223 ) -> i32;
224 fn vt_frame_processor_process_low_latency_super_resolution_with_command_buffer(
225 processor: *mut c_void,
226 command_buffer: *mut c_void,
227 source_frame: *mut c_void,
228 destination_frame: *mut c_void,
229 ) -> i32;
230 fn vt_frame_processor_process_low_latency_frame_interpolation(
231 processor: *mut c_void,
232 source_frame: *mut c_void,
233 previous_frame: *mut c_void,
234 interpolation_phase: *const f32,
235 interpolation_phase_count: usize,
236 destination_frames: *const *mut c_void,
237 destination_frame_count: usize,
238 ) -> i32;
239 fn vt_frame_processor_process_low_latency_frame_interpolation_with_command_buffer(
240 processor: *mut c_void,
241 command_buffer: *mut c_void,
242 source_frame: *mut c_void,
243 previous_frame: *mut c_void,
244 interpolation_phase: *const f32,
245 interpolation_phase_count: usize,
246 destination_frames: *const *mut c_void,
247 destination_frame_count: usize,
248 ) -> i32;
249 fn vt_frame_processor_process_optical_flow(
250 processor: *mut c_void,
251 source_frame: *mut c_void,
252 next_frame: *mut c_void,
253 submission_mode: i32,
254 destination_optical_flow: *mut c_void,
255 ) -> i32;
256 fn vt_frame_processor_process_optical_flow_with_command_buffer(
257 processor: *mut c_void,
258 command_buffer: *mut c_void,
259 source_frame: *mut c_void,
260 next_frame: *mut c_void,
261 submission_mode: i32,
262 destination_optical_flow: *mut c_void,
263 ) -> i32;
264
265 fn vt_frame_processor_end(processor: *mut c_void);
266 fn vt_frame_processor_release(processor: *mut c_void);
267}
268
269#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
271#[allow(clippy::struct_excessive_bools)]
272pub struct FrameProcessorCapabilities {
273 pub super_resolution: bool,
274 pub motion_blur: bool,
275 pub temporal_noise_filter: bool,
276 pub frame_rate_conversion: bool,
277 pub low_latency_super_resolution: bool,
278 pub low_latency_frame_interpolation: bool,
279 pub optical_flow: bool,
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285#[repr(i32)]
286pub enum FrameProcessorSubmissionMode {
287 Random = 1,
288 Sequential = 2,
289}
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq)]
293#[repr(i32)]
294pub enum FrameRateConversionSubmissionMode {
295 Random = 1,
296 Sequential = 2,
297 SequentialReferencesUnchanged = 3,
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq)]
302#[repr(isize)]
303pub enum VTFrameRateConversionConfigurationQualityPrioritization {
304 Normal = ffi::VTFrameRateConversionConfigurationQualityPrioritizationNormal,
305 Quality = ffi::VTFrameRateConversionConfigurationQualityPrioritizationQuality,
306}
307
308#[derive(Debug, Clone, Copy, PartialEq, Eq)]
310#[repr(isize)]
311pub enum VTFrameRateConversionConfigurationRevision {
312 Revision1 = ffi::VTFrameRateConversionConfigurationRevision1,
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317#[repr(isize)]
318pub enum VTMotionBlurConfigurationQualityPrioritization {
319 Normal = ffi::VTMotionBlurConfigurationQualityPrioritizationNormal,
320 Quality = ffi::VTMotionBlurConfigurationQualityPrioritizationQuality,
321}
322
323#[derive(Debug, Clone, Copy, PartialEq, Eq)]
325#[repr(isize)]
326pub enum VTMotionBlurConfigurationRevision {
327 Revision1 = ffi::VTMotionBlurConfigurationRevision1,
328}
329
330#[derive(Debug, Clone, Copy, PartialEq, Eq)]
332#[repr(isize)]
333pub enum VTOpticalFlowConfigurationQualityPrioritization {
334 Normal = ffi::VTOpticalFlowConfigurationQualityPrioritizationNormal,
335 Quality = ffi::VTOpticalFlowConfigurationQualityPrioritizationQuality,
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
340#[repr(isize)]
341pub enum VTOpticalFlowConfigurationRevision {
342 Revision1 = ffi::VTOpticalFlowConfigurationRevision1,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347#[repr(isize)]
348pub enum VTSuperResolutionScalerConfigurationInputType {
349 Video = ffi::VTSuperResolutionScalerConfigurationInputTypeVideo,
350 Image = ffi::VTSuperResolutionScalerConfigurationInputTypeImage,
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq)]
355#[repr(isize)]
356pub enum VTSuperResolutionScalerConfigurationQualityPrioritization {
357 Normal = ffi::VTSuperResolutionScalerConfigurationQualityPrioritizationNormal,
358}
359
360#[derive(Debug, Clone, Copy, PartialEq, Eq)]
362#[repr(isize)]
363pub enum VTSuperResolutionScalerConfigurationRevision {
364 Revision1 = ffi::VTSuperResolutionScalerConfigurationRevision1,
365}
366
367#[derive(Debug, Clone, Copy, PartialEq, Eq)]
369pub struct SuperResolutionConfiguration {
370 pub frame_width: usize,
371 pub frame_height: usize,
372 pub scale_factor: usize,
373 pub use_precomputed_flow: bool,
374 pub input_type: VTSuperResolutionScalerConfigurationInputType,
375 pub quality_prioritization: VTSuperResolutionScalerConfigurationQualityPrioritization,
376 pub revision: VTSuperResolutionScalerConfigurationRevision,
377}
378
379impl SuperResolutionConfiguration {
380 #[must_use]
381 pub const fn new(frame_width: usize, frame_height: usize, scale_factor: usize) -> Self {
382 Self {
383 frame_width,
384 frame_height,
385 scale_factor,
386 use_precomputed_flow: false,
387 input_type: VTSuperResolutionScalerConfigurationInputType::Video,
388 quality_prioritization:
389 VTSuperResolutionScalerConfigurationQualityPrioritization::Normal,
390 revision: VTSuperResolutionScalerConfigurationRevision::Revision1,
391 }
392 }
393}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397pub struct MotionBlurConfiguration {
398 pub frame_width: usize,
399 pub frame_height: usize,
400 pub use_precomputed_flow: bool,
401 pub quality_prioritization: VTMotionBlurConfigurationQualityPrioritization,
402 pub revision: VTMotionBlurConfigurationRevision,
403}
404
405impl MotionBlurConfiguration {
406 #[must_use]
407 pub const fn new(frame_width: usize, frame_height: usize) -> Self {
408 Self {
409 frame_width,
410 frame_height,
411 use_precomputed_flow: false,
412 quality_prioritization: VTMotionBlurConfigurationQualityPrioritization::Normal,
413 revision: VTMotionBlurConfigurationRevision::Revision1,
414 }
415 }
416}
417
418#[derive(Debug, Clone, Copy, PartialEq, Eq)]
420pub struct FrameRateConversionConfiguration {
421 pub frame_width: usize,
422 pub frame_height: usize,
423 pub use_precomputed_flow: bool,
424 pub quality_prioritization: VTFrameRateConversionConfigurationQualityPrioritization,
425 pub revision: VTFrameRateConversionConfigurationRevision,
426}
427
428impl FrameRateConversionConfiguration {
429 #[must_use]
430 pub const fn new(frame_width: usize, frame_height: usize) -> Self {
431 Self {
432 frame_width,
433 frame_height,
434 use_precomputed_flow: false,
435 quality_prioritization: VTFrameRateConversionConfigurationQualityPrioritization::Normal,
436 revision: VTFrameRateConversionConfigurationRevision::Revision1,
437 }
438 }
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq)]
443pub struct OpticalFlowConfiguration {
444 pub frame_width: usize,
445 pub frame_height: usize,
446 pub quality_prioritization: VTOpticalFlowConfigurationQualityPrioritization,
447 pub revision: VTOpticalFlowConfigurationRevision,
448}
449
450impl OpticalFlowConfiguration {
451 #[must_use]
452 pub const fn new(frame_width: usize, frame_height: usize) -> Self {
453 Self {
454 frame_width,
455 frame_height,
456 quality_prioritization: VTOpticalFlowConfigurationQualityPrioritization::Normal,
457 revision: VTOpticalFlowConfigurationRevision::Revision1,
458 }
459 }
460}
461
462#[derive(Debug, Clone, Copy, PartialEq, Eq)]
464#[repr(i32)]
465pub enum SuperResolutionModelStatus {
466 DownloadRequired = 0,
467 Downloading = 1,
468 Ready = 2,
469}
470
471impl SuperResolutionModelStatus {
472 fn from_raw(raw: i32) -> Option<Self> {
473 match raw {
474 0 => Some(Self::DownloadRequired),
475 1 => Some(Self::Downloading),
476 2 => Some(Self::Ready),
477 _ => None,
478 }
479 }
480}
481
482#[must_use]
484pub fn frame_processor_capabilities() -> FrameProcessorCapabilities {
485 unsafe {
486 FrameProcessorCapabilities {
487 super_resolution: vt_super_resolution_is_supported(),
488 motion_blur: vt_motion_blur_is_supported(),
489 temporal_noise_filter: vt_temporal_noise_filter_is_supported(),
490 frame_rate_conversion: vt_frame_rate_conversion_is_supported(),
491 low_latency_super_resolution: vt_low_latency_super_resolution_is_supported(),
492 low_latency_frame_interpolation: vt_low_latency_frame_interpolation_is_supported(),
493 optical_flow: vt_optical_flow_is_supported(),
494 }
495 }
496}
497
498#[must_use]
500pub fn super_resolution_supported_scale_factors() -> Vec<u32> {
501 const MAX: usize = 16;
502 let mut buf = vec![0u32; MAX];
503 let n = unsafe { vt_super_resolution_supported_scale_factors(buf.as_mut_ptr(), MAX) };
504 buf.truncate(n);
505 buf
506}
507
508#[must_use]
511pub fn low_latency_super_resolution_supported_scale_factors(
512 frame_width: usize,
513 frame_height: usize,
514) -> Vec<f32> {
515 const MAX: usize = 16;
516 let mut buf = vec![0f32; MAX];
517 let n = unsafe {
518 vt_low_latency_super_resolution_supported_scale_factors(
519 frame_width as isize,
520 frame_height as isize,
521 buf.as_mut_ptr(),
522 MAX,
523 )
524 };
525 buf.truncate(n);
526 buf
527}
528
529#[must_use]
531pub fn super_resolution_model_status(
532 frame_width: usize,
533 frame_height: usize,
534 scale_factor: usize,
535 use_precomputed_flow: bool,
536 input_is_image: bool,
537) -> Option<SuperResolutionModelStatus> {
538 super_resolution_model_status_for_configuration(default_super_resolution_configuration(
539 frame_width,
540 frame_height,
541 scale_factor,
542 use_precomputed_flow,
543 input_is_image,
544 ))
545}
546
547#[must_use]
549pub fn super_resolution_model_status_for_configuration(
550 configuration: SuperResolutionConfiguration,
551) -> Option<SuperResolutionModelStatus> {
552 SuperResolutionModelStatus::from_raw(unsafe {
553 vt_super_resolution_model_status(
554 configuration.frame_width as isize,
555 configuration.frame_height as isize,
556 configuration.scale_factor as isize,
557 configuration.use_precomputed_flow,
558 configuration.input_type as isize,
559 configuration.quality_prioritization as isize,
560 configuration.revision as isize,
561 )
562 })
563}
564
565#[must_use]
567pub fn super_resolution_model_percentage_available(
568 frame_width: usize,
569 frame_height: usize,
570 scale_factor: usize,
571 use_precomputed_flow: bool,
572 input_is_image: bool,
573) -> Option<f32> {
574 super_resolution_model_percentage_available_for_configuration(
575 default_super_resolution_configuration(
576 frame_width,
577 frame_height,
578 scale_factor,
579 use_precomputed_flow,
580 input_is_image,
581 ),
582 )
583}
584
585#[must_use]
587pub fn super_resolution_model_percentage_available_for_configuration(
588 configuration: SuperResolutionConfiguration,
589) -> Option<f32> {
590 let value = unsafe {
591 vt_super_resolution_model_percentage_available(
592 configuration.frame_width as isize,
593 configuration.frame_height as isize,
594 configuration.scale_factor as isize,
595 configuration.use_precomputed_flow,
596 configuration.input_type as isize,
597 configuration.quality_prioritization as isize,
598 configuration.revision as isize,
599 )
600 };
601 (value >= 0.0).then_some(value)
602}
603
604pub fn download_super_resolution_model(
611 frame_width: usize,
612 frame_height: usize,
613 scale_factor: usize,
614 use_precomputed_flow: bool,
615 input_is_image: bool,
616) -> Result<(), VTError> {
617 download_super_resolution_model_for_configuration(default_super_resolution_configuration(
618 frame_width,
619 frame_height,
620 scale_factor,
621 use_precomputed_flow,
622 input_is_image,
623 ))
624}
625
626pub fn download_super_resolution_model_for_configuration(
632 configuration: SuperResolutionConfiguration,
633) -> Result<(), VTError> {
634 let status = unsafe {
635 vt_super_resolution_download_model(
636 configuration.frame_width as isize,
637 configuration.frame_height as isize,
638 configuration.scale_factor as isize,
639 configuration.use_precomputed_flow,
640 configuration.input_type as isize,
641 configuration.quality_prioritization as isize,
642 configuration.revision as isize,
643 )
644 };
645 api_result("downloadConfigurationModelWithCompletionHandler", status)
646}
647
648pub struct FrameProcessorFrame {
650 inner: *mut c_void,
651 buffer: CVPixelBuffer,
652 presentation_time_stamp: CMTime,
653}
654
655unsafe impl Send for FrameProcessorFrame {}
656unsafe impl Sync for FrameProcessorFrame {}
657
658impl Drop for FrameProcessorFrame {
659 fn drop(&mut self) {
660 if !self.inner.is_null() {
661 unsafe { vt_frame_processor_frame_release(self.inner) };
662 self.inner = core::ptr::null_mut();
663 }
664 }
665}
666
667impl FrameProcessorFrame {
668 pub fn new(buffer: &CVPixelBuffer, presentation_time_stamp: CMTime) -> Result<Self, VTError> {
675 if !buffer.is_backed_by_io_surface() {
676 return Err(VTError::InvalidArgument(
677 "VTFrameProcessorFrame requires an IOSurface-backed CVPixelBuffer".to_string(),
678 ));
679 }
680
681 let mut out = core::ptr::null_mut();
682 let status = unsafe {
683 vt_frame_processor_frame_create(
684 buffer.as_ptr(),
685 to_ffi_time(presentation_time_stamp),
686 &raw mut out,
687 )
688 };
689 if status != 0 || out.is_null() {
690 return Err(VTError::ApiFailed {
691 api: "VTFrameProcessorFrame.init(buffer:presentationTimeStamp:)",
692 status,
693 });
694 }
695
696 Ok(Self {
697 inner: out,
698 buffer: buffer.clone(),
699 presentation_time_stamp,
700 })
701 }
702
703 #[must_use]
704 pub fn buffer(&self) -> &CVPixelBuffer {
705 &self.buffer
706 }
707
708 #[must_use]
709 pub const fn presentation_time_stamp(&self) -> CMTime {
710 self.presentation_time_stamp
711 }
712
713 #[must_use]
714 pub const fn as_ptr(&self) -> *mut c_void {
715 self.inner
716 }
717}
718
719pub struct FrameProcessorOpticalFlow {
721 inner: *mut c_void,
722 forward_flow: CVPixelBuffer,
723 backward_flow: CVPixelBuffer,
724}
725
726unsafe impl Send for FrameProcessorOpticalFlow {}
727unsafe impl Sync for FrameProcessorOpticalFlow {}
728
729impl Drop for FrameProcessorOpticalFlow {
730 fn drop(&mut self) {
731 if !self.inner.is_null() {
732 unsafe { vt_frame_processor_optical_flow_release(self.inner) };
733 self.inner = core::ptr::null_mut();
734 }
735 }
736}
737
738impl FrameProcessorOpticalFlow {
739 pub fn new(
746 forward_flow: &CVPixelBuffer,
747 backward_flow: &CVPixelBuffer,
748 ) -> Result<Self, VTError> {
749 if !forward_flow.is_backed_by_io_surface() || !backward_flow.is_backed_by_io_surface() {
750 return Err(VTError::InvalidArgument(
751 "VTFrameProcessorOpticalFlow requires IOSurface-backed CVPixelBuffers".to_string(),
752 ));
753 }
754
755 let mut out = core::ptr::null_mut();
756 let status = unsafe {
757 vt_frame_processor_optical_flow_create(
758 forward_flow.as_ptr(),
759 backward_flow.as_ptr(),
760 &raw mut out,
761 )
762 };
763 if status != 0 || out.is_null() {
764 return Err(VTError::ApiFailed {
765 api: "VTFrameProcessorOpticalFlow.init(forwardFlow:backwardFlow:)",
766 status,
767 });
768 }
769
770 Ok(Self {
771 inner: out,
772 forward_flow: forward_flow.clone(),
773 backward_flow: backward_flow.clone(),
774 })
775 }
776
777 #[must_use]
778 pub fn forward_flow(&self) -> &CVPixelBuffer {
779 &self.forward_flow
780 }
781
782 #[must_use]
783 pub fn backward_flow(&self) -> &CVPixelBuffer {
784 &self.backward_flow
785 }
786
787 #[must_use]
788 pub const fn as_ptr(&self) -> *mut c_void {
789 self.inner
790 }
791}
792
793pub struct FrameProcessor {
795 inner: *mut c_void,
796}
797
798unsafe impl Send for FrameProcessor {}
799unsafe impl Sync for FrameProcessor {}
800
801impl Drop for FrameProcessor {
802 fn drop(&mut self) {
803 if !self.inner.is_null() {
804 unsafe {
805 vt_frame_processor_end(self.inner);
806 vt_frame_processor_release(self.inner);
807 }
808 self.inner = core::ptr::null_mut();
809 }
810 }
811}
812
813impl FrameProcessor {
814 fn from_status(status: i32, ptr: *mut c_void) -> Result<Self, VTError> {
815 if status != 0 || ptr.is_null() {
816 return Err(VTError::SessionCreateFailed(status));
817 }
818 Ok(Self { inner: ptr })
819 }
820
821 pub fn start_super_resolution(
823 frame_width: usize,
824 frame_height: usize,
825 scale_factor: usize,
826 use_precomputed_flow: bool,
827 input_is_image: bool,
828 ) -> Result<Self, VTError> {
829 Self::start_super_resolution_with_configuration(default_super_resolution_configuration(
830 frame_width,
831 frame_height,
832 scale_factor,
833 use_precomputed_flow,
834 input_is_image,
835 ))
836 }
837
838 pub fn start_super_resolution_with_configuration(
840 configuration: SuperResolutionConfiguration,
841 ) -> Result<Self, VTError> {
842 let mut out = core::ptr::null_mut();
843 let status = unsafe {
844 vt_super_resolution_start(
845 configuration.frame_width as isize,
846 configuration.frame_height as isize,
847 configuration.scale_factor as isize,
848 configuration.use_precomputed_flow,
849 configuration.input_type as isize,
850 configuration.quality_prioritization as isize,
851 configuration.revision as isize,
852 &raw mut out,
853 )
854 };
855 Self::from_status(status, out)
856 }
857
858 pub fn start_motion_blur(
860 frame_width: usize,
861 frame_height: usize,
862 use_precomputed_flow: bool,
863 ) -> Result<Self, VTError> {
864 let mut configuration = MotionBlurConfiguration::new(frame_width, frame_height);
865 configuration.use_precomputed_flow = use_precomputed_flow;
866 Self::start_motion_blur_with_configuration(configuration)
867 }
868
869 pub fn start_motion_blur_with_configuration(
871 configuration: MotionBlurConfiguration,
872 ) -> Result<Self, VTError> {
873 let mut out = core::ptr::null_mut();
874 let status = unsafe {
875 vt_motion_blur_start(
876 configuration.frame_width as isize,
877 configuration.frame_height as isize,
878 configuration.use_precomputed_flow,
879 configuration.quality_prioritization as isize,
880 configuration.revision as isize,
881 &raw mut out,
882 )
883 };
884 Self::from_status(status, out)
885 }
886
887 pub fn start_temporal_noise_filter(
889 frame_width: usize,
890 frame_height: usize,
891 source_pixel_format: u32,
892 ) -> Result<Self, VTError> {
893 let mut out = core::ptr::null_mut();
894 let status = unsafe {
895 vt_temporal_noise_filter_start(
896 frame_width as isize,
897 frame_height as isize,
898 source_pixel_format,
899 &raw mut out,
900 )
901 };
902 Self::from_status(status, out)
903 }
904
905 pub fn start_frame_rate_conversion(
907 frame_width: usize,
908 frame_height: usize,
909 use_precomputed_flow: bool,
910 ) -> Result<Self, VTError> {
911 let mut configuration = FrameRateConversionConfiguration::new(frame_width, frame_height);
912 configuration.use_precomputed_flow = use_precomputed_flow;
913 Self::start_frame_rate_conversion_with_configuration(configuration)
914 }
915
916 pub fn start_frame_rate_conversion_with_configuration(
918 configuration: FrameRateConversionConfiguration,
919 ) -> Result<Self, VTError> {
920 let mut out = core::ptr::null_mut();
921 let status = unsafe {
922 vt_frame_rate_conversion_start(
923 configuration.frame_width as isize,
924 configuration.frame_height as isize,
925 configuration.use_precomputed_flow,
926 configuration.quality_prioritization as isize,
927 configuration.revision as isize,
928 &raw mut out,
929 )
930 };
931 Self::from_status(status, out)
932 }
933
934 pub fn start_low_latency_super_resolution(
936 frame_width: usize,
937 frame_height: usize,
938 scale_factor: f32,
939 ) -> Result<Self, VTError> {
940 let mut out = core::ptr::null_mut();
941 let status = unsafe {
942 vt_low_latency_super_resolution_start(
943 frame_width as isize,
944 frame_height as isize,
945 scale_factor,
946 &raw mut out,
947 )
948 };
949 Self::from_status(status, out)
950 }
951
952 pub fn start_low_latency_frame_interpolation(
954 frame_width: usize,
955 frame_height: usize,
956 number_of_interpolated_frames: usize,
957 ) -> Result<Self, VTError> {
958 let mut out = core::ptr::null_mut();
959 let status = unsafe {
960 vt_low_latency_frame_interpolation_start(
961 frame_width as isize,
962 frame_height as isize,
963 number_of_interpolated_frames as isize,
964 &raw mut out,
965 )
966 };
967 Self::from_status(status, out)
968 }
969
970 pub fn start_optical_flow(frame_width: usize, frame_height: usize) -> Result<Self, VTError> {
972 Self::start_optical_flow_with_configuration(OpticalFlowConfiguration::new(
973 frame_width,
974 frame_height,
975 ))
976 }
977
978 pub fn start_optical_flow_with_configuration(
980 configuration: OpticalFlowConfiguration,
981 ) -> Result<Self, VTError> {
982 let mut out = core::ptr::null_mut();
983 let status = unsafe {
984 vt_optical_flow_start(
985 configuration.frame_width as isize,
986 configuration.frame_height as isize,
987 configuration.quality_prioritization as isize,
988 configuration.revision as isize,
989 &raw mut out,
990 )
991 };
992 Self::from_status(status, out)
993 }
994
995 pub fn end_session(&self) {
997 unsafe { vt_frame_processor_end(self.inner) };
998 }
999
1000 pub fn process_super_resolution(
1002 &self,
1003 source_frame: &FrameProcessorFrame,
1004 previous_frame: Option<&FrameProcessorFrame>,
1005 previous_output_frame: Option<&FrameProcessorFrame>,
1006 optical_flow: Option<&FrameProcessorOpticalFlow>,
1007 submission_mode: FrameProcessorSubmissionMode,
1008 destination_frame: &FrameProcessorFrame,
1009 ) -> Result<(), VTError> {
1010 api_result("VTSuperResolutionScalerParameters", unsafe {
1011 vt_frame_processor_process_super_resolution(
1012 self.inner,
1013 source_frame.as_ptr(),
1014 frame_ptr(previous_frame),
1015 frame_ptr(previous_output_frame),
1016 flow_ptr(optical_flow),
1017 submission_mode as i32,
1018 destination_frame.as_ptr(),
1019 )
1020 })
1021 }
1022
1023 pub fn process_super_resolution_with_command_buffer(
1025 &self,
1026 command_buffer: &CommandBuffer,
1027 source_frame: &FrameProcessorFrame,
1028 previous_frame: Option<&FrameProcessorFrame>,
1029 previous_output_frame: Option<&FrameProcessorFrame>,
1030 optical_flow: Option<&FrameProcessorOpticalFlow>,
1031 submission_mode: FrameProcessorSubmissionMode,
1032 destination_frame: &FrameProcessorFrame,
1033 ) -> Result<(), VTError> {
1034 process_with_command_buffer(
1035 "process(with:parameters:) super-resolution",
1036 command_buffer,
1037 |command_buffer| unsafe {
1038 vt_frame_processor_process_super_resolution_with_command_buffer(
1039 self.inner,
1040 command_buffer,
1041 source_frame.as_ptr(),
1042 frame_ptr(previous_frame),
1043 frame_ptr(previous_output_frame),
1044 flow_ptr(optical_flow),
1045 submission_mode as i32,
1046 destination_frame.as_ptr(),
1047 )
1048 },
1049 )
1050 }
1051
1052 #[allow(clippy::too_many_arguments)]
1054 pub fn process_motion_blur(
1055 &self,
1056 source_frame: &FrameProcessorFrame,
1057 next_frame: Option<&FrameProcessorFrame>,
1058 previous_frame: Option<&FrameProcessorFrame>,
1059 next_optical_flow: Option<&FrameProcessorOpticalFlow>,
1060 previous_optical_flow: Option<&FrameProcessorOpticalFlow>,
1061 motion_blur_strength: usize,
1062 submission_mode: FrameProcessorSubmissionMode,
1063 destination_frame: &FrameProcessorFrame,
1064 ) -> Result<(), VTError> {
1065 api_result("VTMotionBlurParameters", unsafe {
1066 vt_frame_processor_process_motion_blur(
1067 self.inner,
1068 source_frame.as_ptr(),
1069 frame_ptr(next_frame),
1070 frame_ptr(previous_frame),
1071 flow_ptr(next_optical_flow),
1072 flow_ptr(previous_optical_flow),
1073 motion_blur_strength as isize,
1074 submission_mode as i32,
1075 destination_frame.as_ptr(),
1076 )
1077 })
1078 }
1079
1080 #[allow(clippy::too_many_arguments)]
1082 pub fn process_motion_blur_with_command_buffer(
1083 &self,
1084 command_buffer: &CommandBuffer,
1085 source_frame: &FrameProcessorFrame,
1086 next_frame: Option<&FrameProcessorFrame>,
1087 previous_frame: Option<&FrameProcessorFrame>,
1088 next_optical_flow: Option<&FrameProcessorOpticalFlow>,
1089 previous_optical_flow: Option<&FrameProcessorOpticalFlow>,
1090 motion_blur_strength: usize,
1091 submission_mode: FrameProcessorSubmissionMode,
1092 destination_frame: &FrameProcessorFrame,
1093 ) -> Result<(), VTError> {
1094 process_with_command_buffer(
1095 "process(with:parameters:) motion-blur",
1096 command_buffer,
1097 |command_buffer| unsafe {
1098 vt_frame_processor_process_motion_blur_with_command_buffer(
1099 self.inner,
1100 command_buffer,
1101 source_frame.as_ptr(),
1102 frame_ptr(next_frame),
1103 frame_ptr(previous_frame),
1104 flow_ptr(next_optical_flow),
1105 flow_ptr(previous_optical_flow),
1106 motion_blur_strength as isize,
1107 submission_mode as i32,
1108 destination_frame.as_ptr(),
1109 )
1110 },
1111 )
1112 }
1113
1114 pub fn process_temporal_noise_filter(
1116 &self,
1117 source_frame: &FrameProcessorFrame,
1118 next_frames: &[FrameProcessorFrame],
1119 previous_frames: &[FrameProcessorFrame],
1120 destination_frame: &FrameProcessorFrame,
1121 filter_strength: f32,
1122 has_discontinuity: bool,
1123 ) -> Result<(), VTError> {
1124 let next_ptrs = frame_slice_ptrs(next_frames);
1125 let previous_ptrs = frame_slice_ptrs(previous_frames);
1126 api_result("VTTemporalNoiseFilterParameters", unsafe {
1127 vt_frame_processor_process_temporal_noise_filter(
1128 self.inner,
1129 source_frame.as_ptr(),
1130 next_ptrs.as_ptr(),
1131 next_ptrs.len(),
1132 previous_ptrs.as_ptr(),
1133 previous_ptrs.len(),
1134 destination_frame.as_ptr(),
1135 filter_strength,
1136 has_discontinuity,
1137 )
1138 })
1139 }
1140
1141 pub fn process_temporal_noise_filter_with_command_buffer(
1143 &self,
1144 command_buffer: &CommandBuffer,
1145 source_frame: &FrameProcessorFrame,
1146 next_frames: &[FrameProcessorFrame],
1147 previous_frames: &[FrameProcessorFrame],
1148 destination_frame: &FrameProcessorFrame,
1149 filter_strength: f32,
1150 has_discontinuity: bool,
1151 ) -> Result<(), VTError> {
1152 let next_ptrs = frame_slice_ptrs(next_frames);
1153 let previous_ptrs = frame_slice_ptrs(previous_frames);
1154 process_with_command_buffer(
1155 "process(with:parameters:) temporal-noise-filter",
1156 command_buffer,
1157 |command_buffer| unsafe {
1158 vt_frame_processor_process_temporal_noise_filter_with_command_buffer(
1159 self.inner,
1160 command_buffer,
1161 source_frame.as_ptr(),
1162 next_ptrs.as_ptr(),
1163 next_ptrs.len(),
1164 previous_ptrs.as_ptr(),
1165 previous_ptrs.len(),
1166 destination_frame.as_ptr(),
1167 filter_strength,
1168 has_discontinuity,
1169 )
1170 },
1171 )
1172 }
1173
1174 pub fn process_frame_rate_conversion(
1176 &self,
1177 source_frame: &FrameProcessorFrame,
1178 next_frame: Option<&FrameProcessorFrame>,
1179 optical_flow: Option<&FrameProcessorOpticalFlow>,
1180 interpolation_phase: &[f32],
1181 submission_mode: FrameRateConversionSubmissionMode,
1182 destination_frames: &[FrameProcessorFrame],
1183 ) -> Result<(), VTError> {
1184 validate_parallel_lengths(
1185 "interpolation_phase",
1186 interpolation_phase,
1187 destination_frames,
1188 )?;
1189 let destination_ptrs = frame_slice_ptrs(destination_frames);
1190 api_result("VTFrameRateConversionParameters", unsafe {
1191 vt_frame_processor_process_frame_rate_conversion(
1192 self.inner,
1193 source_frame.as_ptr(),
1194 frame_ptr(next_frame),
1195 flow_ptr(optical_flow),
1196 interpolation_phase.as_ptr(),
1197 interpolation_phase.len(),
1198 submission_mode as i32,
1199 destination_ptrs.as_ptr(),
1200 destination_ptrs.len(),
1201 )
1202 })
1203 }
1204
1205 pub fn process_frame_rate_conversion_with_command_buffer(
1207 &self,
1208 command_buffer: &CommandBuffer,
1209 source_frame: &FrameProcessorFrame,
1210 next_frame: Option<&FrameProcessorFrame>,
1211 optical_flow: Option<&FrameProcessorOpticalFlow>,
1212 interpolation_phase: &[f32],
1213 submission_mode: FrameRateConversionSubmissionMode,
1214 destination_frames: &[FrameProcessorFrame],
1215 ) -> Result<(), VTError> {
1216 validate_parallel_lengths(
1217 "interpolation_phase",
1218 interpolation_phase,
1219 destination_frames,
1220 )?;
1221 let destination_ptrs = frame_slice_ptrs(destination_frames);
1222 process_with_command_buffer(
1223 "process(with:parameters:) frame-rate-conversion",
1224 command_buffer,
1225 |command_buffer| unsafe {
1226 vt_frame_processor_process_frame_rate_conversion_with_command_buffer(
1227 self.inner,
1228 command_buffer,
1229 source_frame.as_ptr(),
1230 frame_ptr(next_frame),
1231 flow_ptr(optical_flow),
1232 interpolation_phase.as_ptr(),
1233 interpolation_phase.len(),
1234 submission_mode as i32,
1235 destination_ptrs.as_ptr(),
1236 destination_ptrs.len(),
1237 )
1238 },
1239 )
1240 }
1241
1242 pub fn process_low_latency_super_resolution(
1244 &self,
1245 source_frame: &FrameProcessorFrame,
1246 destination_frame: &FrameProcessorFrame,
1247 ) -> Result<(), VTError> {
1248 api_result("VTLowLatencySuperResolutionScalerParameters", unsafe {
1249 vt_frame_processor_process_low_latency_super_resolution(
1250 self.inner,
1251 source_frame.as_ptr(),
1252 destination_frame.as_ptr(),
1253 )
1254 })
1255 }
1256
1257 pub fn process_low_latency_super_resolution_with_command_buffer(
1259 &self,
1260 command_buffer: &CommandBuffer,
1261 source_frame: &FrameProcessorFrame,
1262 destination_frame: &FrameProcessorFrame,
1263 ) -> Result<(), VTError> {
1264 process_with_command_buffer(
1265 "process(with:parameters:) low-latency-super-resolution",
1266 command_buffer,
1267 |command_buffer| unsafe {
1268 vt_frame_processor_process_low_latency_super_resolution_with_command_buffer(
1269 self.inner,
1270 command_buffer,
1271 source_frame.as_ptr(),
1272 destination_frame.as_ptr(),
1273 )
1274 },
1275 )
1276 }
1277
1278 pub fn process_low_latency_frame_interpolation(
1280 &self,
1281 source_frame: &FrameProcessorFrame,
1282 previous_frame: &FrameProcessorFrame,
1283 interpolation_phase: &[f32],
1284 destination_frames: &[FrameProcessorFrame],
1285 ) -> Result<(), VTError> {
1286 validate_parallel_lengths(
1287 "interpolation_phase",
1288 interpolation_phase,
1289 destination_frames,
1290 )?;
1291 let destination_ptrs = frame_slice_ptrs(destination_frames);
1292 api_result("VTLowLatencyFrameInterpolationParameters", unsafe {
1293 vt_frame_processor_process_low_latency_frame_interpolation(
1294 self.inner,
1295 source_frame.as_ptr(),
1296 previous_frame.as_ptr(),
1297 interpolation_phase.as_ptr(),
1298 interpolation_phase.len(),
1299 destination_ptrs.as_ptr(),
1300 destination_ptrs.len(),
1301 )
1302 })
1303 }
1304
1305 pub fn process_low_latency_frame_interpolation_with_command_buffer(
1307 &self,
1308 command_buffer: &CommandBuffer,
1309 source_frame: &FrameProcessorFrame,
1310 previous_frame: &FrameProcessorFrame,
1311 interpolation_phase: &[f32],
1312 destination_frames: &[FrameProcessorFrame],
1313 ) -> Result<(), VTError> {
1314 validate_parallel_lengths(
1315 "interpolation_phase",
1316 interpolation_phase,
1317 destination_frames,
1318 )?;
1319 let destination_ptrs = frame_slice_ptrs(destination_frames);
1320 process_with_command_buffer(
1321 "process(with:parameters:) low-latency-frame-interpolation",
1322 command_buffer,
1323 |command_buffer| unsafe {
1324 vt_frame_processor_process_low_latency_frame_interpolation_with_command_buffer(
1325 self.inner,
1326 command_buffer,
1327 source_frame.as_ptr(),
1328 previous_frame.as_ptr(),
1329 interpolation_phase.as_ptr(),
1330 interpolation_phase.len(),
1331 destination_ptrs.as_ptr(),
1332 destination_ptrs.len(),
1333 )
1334 },
1335 )
1336 }
1337
1338 pub fn process_optical_flow(
1340 &self,
1341 source_frame: &FrameProcessorFrame,
1342 next_frame: &FrameProcessorFrame,
1343 submission_mode: FrameProcessorSubmissionMode,
1344 destination_optical_flow: &FrameProcessorOpticalFlow,
1345 ) -> Result<(), VTError> {
1346 api_result("VTOpticalFlowParameters", unsafe {
1347 vt_frame_processor_process_optical_flow(
1348 self.inner,
1349 source_frame.as_ptr(),
1350 next_frame.as_ptr(),
1351 submission_mode as i32,
1352 destination_optical_flow.as_ptr(),
1353 )
1354 })
1355 }
1356
1357 pub fn process_optical_flow_with_command_buffer(
1359 &self,
1360 command_buffer: &CommandBuffer,
1361 source_frame: &FrameProcessorFrame,
1362 next_frame: &FrameProcessorFrame,
1363 submission_mode: FrameProcessorSubmissionMode,
1364 destination_optical_flow: &FrameProcessorOpticalFlow,
1365 ) -> Result<(), VTError> {
1366 process_with_command_buffer(
1367 "process(with:parameters:) optical-flow",
1368 command_buffer,
1369 |command_buffer| unsafe {
1370 vt_frame_processor_process_optical_flow_with_command_buffer(
1371 self.inner,
1372 command_buffer,
1373 source_frame.as_ptr(),
1374 next_frame.as_ptr(),
1375 submission_mode as i32,
1376 destination_optical_flow.as_ptr(),
1377 )
1378 },
1379 )
1380 }
1381
1382 #[must_use]
1384 pub const fn as_ptr(&self) -> *mut c_void {
1385 self.inner
1386 }
1387}
1388
1389fn default_super_resolution_configuration(
1390 frame_width: usize,
1391 frame_height: usize,
1392 scale_factor: usize,
1393 use_precomputed_flow: bool,
1394 input_is_image: bool,
1395) -> SuperResolutionConfiguration {
1396 let mut configuration =
1397 SuperResolutionConfiguration::new(frame_width, frame_height, scale_factor);
1398 configuration.use_precomputed_flow = use_precomputed_flow;
1399 configuration.input_type = if input_is_image {
1400 VTSuperResolutionScalerConfigurationInputType::Image
1401 } else {
1402 VTSuperResolutionScalerConfigurationInputType::Video
1403 };
1404 configuration
1405}
1406
1407fn to_ffi_time(time: CMTime) -> ffi::CMTime {
1408 time
1409}
1410
1411fn frame_ptr(frame: Option<&FrameProcessorFrame>) -> *mut c_void {
1412 frame.map_or(core::ptr::null_mut(), FrameProcessorFrame::as_ptr)
1413}
1414
1415fn flow_ptr(flow: Option<&FrameProcessorOpticalFlow>) -> *mut c_void {
1416 flow.map_or(core::ptr::null_mut(), FrameProcessorOpticalFlow::as_ptr)
1417}
1418
1419fn frame_slice_ptrs(frames: &[FrameProcessorFrame]) -> Vec<*mut c_void> {
1420 frames.iter().map(FrameProcessorFrame::as_ptr).collect()
1421}
1422
1423fn validate_parallel_lengths<T>(
1424 label: &'static str,
1425 left: &[f32],
1426 right: &[T],
1427) -> Result<(), VTError> {
1428 if left.len() == right.len() {
1429 Ok(())
1430 } else {
1431 Err(VTError::InvalidArgument(format!(
1432 "{label} length ({}) must match destination frame count ({})",
1433 left.len(),
1434 right.len()
1435 )))
1436 }
1437}
1438
1439fn process_with_command_buffer(
1440 api: &'static str,
1441 command_buffer: &CommandBuffer,
1442 process: impl FnOnce(*mut c_void) -> i32,
1443) -> Result<(), VTError> {
1444 let status = command_buffer
1445 .encode_foreign(|foreign| process(foreign.command_buffer()))
1446 .map_err(VTError::CommandBuffer)?;
1447 api_result(api, status)
1448}
1449
1450fn api_result(api: &'static str, status: i32) -> Result<(), VTError> {
1451 if status == 0 {
1452 Ok(())
1453 } else {
1454 Err(VTError::ApiFailed { api, status })
1455 }
1456}