1use std::ptr::{null, null_mut};
2
3use block::{Block, ConcreteBlock};
4use core_foundation::{
5 base::{kCFAllocatorDefault, Boolean, CFAllocatorRef, CFType, CFTypeID, OSStatus, TCFType},
6 declare_TCFType,
7 dictionary::{CFDictionary, CFDictionaryRef},
8 impl_CFTypeDescription, impl_TCFType,
9 string::CFString,
10};
11use core_media::{
12 format_description::{CMFormatDescription, CMFormatDescriptionRef, CMVideoCodecType, CMVideoFormatDescription, CMVideoFormatDescriptionRef},
13 sample_buffer::{CMSampleBuffer, CMSampleBufferRef},
14 time::CMTime,
15};
16use core_video::{
17 image_buffer::{CVImageBuffer, CVImageBufferRef},
18 pixel_buffer::{CVPixelBuffer, CVPixelBufferRef},
19};
20use libc::c_void;
21
22use crate::{
23 base::status_to_result,
24 errors::{VTDecodeFrameFlags, VTDecodeInfoFlags},
25 session::{TVTSession, VTSessionRef},
26};
27
28pub type VTDecompressionSessionRef = VTSessionRef;
29
30#[repr(C, packed(4))]
31#[derive(Debug, Copy, Clone)]
32pub struct VTDecompressionOutputCallbackRecord {
33 pub decompressionOutputCallback: VTDecompressionOutputCallback,
34 pub decompressionOutputRefCon: *mut c_void,
35}
36
37pub type VTDecompressionOutputHandler = *const Block<(OSStatus, VTDecodeInfoFlags, CVImageBufferRef, CMTime, CMTime), ()>;
38
39pub type VTDecompressionOutputCallback = extern "C" fn(
40 decompressionOutputRefCon: *mut c_void,
41 sourceFrameRefCon: *mut c_void,
42 status: OSStatus,
43 infoFlags: VTDecodeInfoFlags,
44 imageBuffer: CVImageBufferRef,
45 presentationTimeStamp: CMTime,
46 presentationDuration: CMTime,
47);
48
49extern "C" {
50 pub fn VTDecompressionSessionCreate(
51 allocator: CFAllocatorRef,
52 videoFormatDescription: CMVideoFormatDescriptionRef,
53 videoDecoderSpecification: CFDictionaryRef,
54 destinationImageBufferAttributes: CFDictionaryRef,
55 outputCallback: *const VTDecompressionOutputCallbackRecord,
56 decompressionSessionOut: *mut VTDecompressionSessionRef,
57 ) -> OSStatus;
58 pub fn VTDecompressionSessionGetTypeID() -> CFTypeID;
59 pub fn VTDecompressionSessionDecodeFrame(
60 session: VTDecompressionSessionRef,
61 sampleBuffer: CMSampleBufferRef,
62 decodeFlags: VTDecodeFrameFlags,
63 sourceFrameRefCon: *mut c_void,
64 infoFlagsOut: *mut VTDecodeInfoFlags,
65 ) -> OSStatus;
66 pub fn VTDecompressionSessionDecodeFrameWithOutputHandler(
67 session: VTDecompressionSessionRef,
68 sampleBuffer: CMSampleBufferRef,
69 decodeFlags: VTDecodeFrameFlags,
70 infoFlagsOut: *mut VTDecodeInfoFlags,
71 outputHandler: VTDecompressionOutputHandler,
72 ) -> OSStatus;
73 pub fn VTDecompressionSessionFinishDelayedFrames(session: VTDecompressionSessionRef) -> OSStatus;
74 pub fn VTDecompressionSessionCanAcceptFormatDescription(session: VTDecompressionSessionRef, newFormatDesc: CMFormatDescriptionRef) -> Boolean;
75 pub fn VTDecompressionSessionWaitForAsynchronousFrames(session: VTDecompressionSessionRef) -> OSStatus;
76 pub fn VTDecompressionSessionCopyBlackPixelBuffer(session: VTDecompressionSessionRef, pixelBufferOut: *mut CVPixelBufferRef) -> OSStatus;
77 pub fn VTIsHardwareDecodeSupported(codecType: CMVideoCodecType) -> Boolean;
78}
79
80declare_TCFType!(VTDecompressionSession, VTDecompressionSessionRef);
81impl_TCFType!(VTDecompressionSession, VTDecompressionSessionRef, VTDecompressionSessionGetTypeID);
82impl_CFTypeDescription!(VTDecompressionSession);
83
84impl TVTSession for VTDecompressionSession {}
85
86impl VTDecompressionSession {
87 pub fn new(
88 video_format_description: CMVideoFormatDescription,
89 video_decoder_specification: Option<&CFDictionary<CFString, CFType>>,
90 destination_image_buffer_attributes: Option<&CFDictionary<CFString, CFType>>,
91 ) -> Result<Self, OSStatus> {
92 unsafe { Self::new_with_callback(video_format_description, video_decoder_specification, destination_image_buffer_attributes, None) }
93 }
94
95 pub unsafe fn new_with_callback(
96 video_format_description: CMVideoFormatDescription,
97 video_decoder_specification: Option<&CFDictionary<CFString, CFType>>,
98 destination_image_buffer_attributes: Option<&CFDictionary<CFString, CFType>>,
99 output_callback: Option<*const VTDecompressionOutputCallbackRecord>,
100 ) -> Result<Self, OSStatus> {
101 let mut session: VTDecompressionSessionRef = null_mut();
102 let status = VTDecompressionSessionCreate(
103 kCFAllocatorDefault,
104 video_format_description.as_concrete_TypeRef(),
105 video_decoder_specification.map_or(null(), |s| s.as_concrete_TypeRef()),
106 destination_image_buffer_attributes.map_or(null(), |a| a.as_concrete_TypeRef()),
107 output_callback.unwrap_or(null()),
108 &mut session,
109 );
110 status_to_result(status).map(|_| TCFType::wrap_under_create_rule(session))
111 }
112
113 pub unsafe fn decode_frame(
114 &self,
115 sample_buffer: CMSampleBuffer,
116 decode_flags: VTDecodeFrameFlags,
117 source_frame_ref_con: Option<*mut c_void>,
118 ) -> Result<VTDecodeInfoFlags, OSStatus> {
119 let mut info_flags_out: VTDecodeInfoFlags = VTDecodeInfoFlags::empty();
120
121 let status = VTDecompressionSessionDecodeFrame(
122 self.as_concrete_TypeRef(),
123 sample_buffer.as_concrete_TypeRef(),
124 decode_flags,
125 source_frame_ref_con.unwrap_or(null_mut()),
126 &mut info_flags_out,
127 );
128
129 status_to_result(status).map(|_| info_flags_out)
130 }
131
132 pub fn decode_frame_with_closure<F>(
133 &self,
134 sample_buffer: CMSampleBuffer,
135 decode_flags: VTDecodeFrameFlags,
136 closure: F,
137 ) -> Result<VTDecodeInfoFlags, OSStatus>
138 where
139 F: Fn(OSStatus, VTDecodeInfoFlags, CVImageBuffer, CMTime, CMTime) + 'static,
140 {
141 let handler = ConcreteBlock::new(
142 move |status: OSStatus, info_flags: VTDecodeInfoFlags, image_buffer: CVImageBufferRef, pts: CMTime, duration: CMTime| {
143 let image_buffer = unsafe { CVImageBuffer::wrap_under_get_rule(image_buffer) };
144 closure(status, info_flags, image_buffer, pts, duration)
145 },
146 )
147 .copy();
148
149 let mut info_flags_out: VTDecodeInfoFlags = VTDecodeInfoFlags::empty();
150
151 let status = unsafe {
152 VTDecompressionSessionDecodeFrameWithOutputHandler(
153 self.as_concrete_TypeRef(),
154 sample_buffer.as_concrete_TypeRef(),
155 decode_flags,
156 &mut info_flags_out,
157 &*handler,
158 )
159 };
160
161 status_to_result(status).map(|_| info_flags_out)
162 }
163
164 pub fn finish_delayed_frames(&self) -> Result<(), OSStatus> {
165 let status = unsafe { VTDecompressionSessionFinishDelayedFrames(self.as_concrete_TypeRef()) };
166 status_to_result(status)
167 }
168
169 pub fn can_accept_format_description(&self, new_format_desc: CMFormatDescription) -> bool {
170 unsafe { VTDecompressionSessionCanAcceptFormatDescription(self.as_concrete_TypeRef(), new_format_desc.as_concrete_TypeRef()) != 0 }
171 }
172
173 pub fn wait_for_asynchronous_frames(&self) -> Result<(), OSStatus> {
174 let status = unsafe { VTDecompressionSessionWaitForAsynchronousFrames(self.as_concrete_TypeRef()) };
175 status_to_result(status)
176 }
177
178 pub fn copy_black_pixel_buffer(&self) -> Result<CVPixelBuffer, OSStatus> {
179 let mut pixel_buffer_out: CVPixelBufferRef = null_mut();
180 let status = unsafe { VTDecompressionSessionCopyBlackPixelBuffer(self.as_concrete_TypeRef(), &mut pixel_buffer_out) };
181 status_to_result(status).map(|_| unsafe { CVPixelBuffer::wrap_under_create_rule(pixel_buffer_out) })
182 }
183
184 pub fn is_hardware_decode_supported(codec_type: CMVideoCodecType) -> bool {
185 unsafe { VTIsHardwareDecodeSupported(codec_type) != 0 }
186 }
187}