1use std::ptr::{null, null_mut};
2
3use bitflags::bitflags;
4use block::{Block, ConcreteBlock};
5use core_foundation::{
6 base::{Boolean, CFAllocator, CFAllocatorRef, CFType, CFTypeID, OSStatus, TCFType},
7 declare_TCFType,
8 dictionary::{CFDictionary, CFDictionaryRef},
9 impl_CFTypeDescription, impl_TCFType,
10 string::{CFString, CFStringRef},
11};
12use core_media::{base::CMItemCount, format_description::CMVideoCodecType, sample_buffer::CMSampleBufferRef, time::CMTime, time_range::CMTimeRange};
13use core_video::{
14 image_buffer::{CVImageBuffer, CVImageBufferRef},
15 pixel_buffer_pool::{CVPixelBufferPool, CVPixelBufferPoolRef},
16};
17use libc::c_void;
18
19use crate::{
20 errors::VTEncodeInfoFlags,
21 session::{TVTSession, VTSessionRef},
22};
23
24pub type VTCompressionSessionRef = VTSessionRef;
25
26pub type VTCompressionOutputCallback = extern "C" fn(
27 outputCallbackRefCon: *mut c_void,
28 sourceFrameRefCon: *mut c_void,
29 status: OSStatus,
30 infoFlags: VTEncodeInfoFlags,
31 sampleBuffer: CMSampleBufferRef,
32);
33
34pub type VTCompressionOutputHandler = *const Block<(OSStatus, VTEncodeInfoFlags, CMSampleBufferRef), ()>;
35
36bitflags! {
37 #[repr(transparent)]
38 #[derive(Clone, Copy, Debug, Default, PartialEq)]
39 pub struct VTCompressionSessionOptionFlags: u32 {
40 #[doc(alias = "kVTCompressionSessionBeginFinalPass")]
41 const BeginFinalPass = 1 << 0;
42 }
43}
44
45extern "C" {
46 pub static kVTVideoEncoderSpecification_EncoderID: CFStringRef;
47
48 pub fn VTCompressionSessionCreate(
49 allocator: CFAllocatorRef,
50 width: i32,
51 height: i32,
52 codecType: CMVideoCodecType,
53 encoderSpecification: CFDictionaryRef,
54 sourceImageBufferAttributes: CFDictionaryRef,
55 compressedDataAllocator: CFAllocatorRef,
56 outputCallback: *const VTCompressionOutputCallback,
57 outputCallbackRefCon: *mut c_void,
58 compressionSessionOut: *mut VTCompressionSessionRef,
59 ) -> OSStatus;
60 pub fn VTCompressionSessionInvalidate(session: VTCompressionSessionRef) -> ();
61 pub fn VTCompressionSessionGetTypeID() -> CFTypeID;
62 pub fn VTCompressionSessionGetPixelBufferPool(session: VTCompressionSessionRef) -> CVPixelBufferPoolRef;
63 pub fn VTCompressionSessionPrepareToEncodeFrames(session: VTCompressionSessionRef) -> OSStatus;
64 pub fn VTCompressionSessionEncodeFrame(
65 session: VTCompressionSessionRef,
66 imageBuffer: CVImageBufferRef,
67 presentationTimeStamp: CMTime,
68 duration: CMTime,
69 frameProperties: CFDictionaryRef,
70 sourceFrameRefcon: *mut c_void,
71 infoFlagsOut: *mut VTEncodeInfoFlags,
72 ) -> OSStatus;
73 pub fn VTCompressionSessionEncodeFrameWithOutputHandler(
74 session: VTCompressionSessionRef,
75 imageBuffer: CVImageBufferRef,
76 presentationTimeStamp: CMTime,
77 duration: CMTime,
78 frameProperties: CFDictionaryRef,
79 infoFlagsOut: *mut VTEncodeInfoFlags,
80 outputHandler: VTCompressionOutputHandler,
81 ) -> OSStatus;
82 pub fn VTCompressionSessionCompleteFrames(session: VTCompressionSessionRef, completeUntilPresentationTimeStamp: CMTime) -> OSStatus;
83 pub fn VTCompressionSessionBeginPass(
84 session: VTCompressionSessionRef,
85 beginPassFlags: VTCompressionSessionOptionFlags,
86 reserved: *mut u32,
87 ) -> OSStatus;
88 pub fn VTCompressionSessionEndPass(session: VTCompressionSessionRef, furtherPassesRequestedOut: *mut Boolean, reserved: *mut u32) -> OSStatus;
89 pub fn VTCompressionSessionGetTimeRangesForNextPass(
90 session: VTCompressionSessionRef,
91 timeRangeCountOut: *mut CMItemCount,
92 timeRangeArrayOut: *const CMTimeRange,
93 ) -> OSStatus;
94}
95
96declare_TCFType!(VTCompressionSession, VTCompressionSessionRef);
97impl_TCFType!(VTCompressionSession, VTCompressionSessionRef, VTCompressionSessionGetTypeID);
98impl_CFTypeDescription!(VTCompressionSession);
99
100impl TVTSession for VTCompressionSession {}
101
102impl VTCompressionSession {
103 pub fn new(
104 width: i32,
105 height: i32,
106 codec_type: CMVideoCodecType,
107 encoder_specification: CFDictionary<CFString, CFType>,
108 source_image_buffer_attributes: CFDictionary<CFString, CFType>,
109 compressed_data_allocator: CFAllocator,
110 ) -> Result<VTCompressionSession, OSStatus> {
111 unsafe {
112 Self::new_with_callback(
113 width,
114 height,
115 codec_type,
116 encoder_specification,
117 source_image_buffer_attributes,
118 compressed_data_allocator,
119 None,
120 None,
121 )
122 }
123 }
124 pub unsafe fn new_with_callback(
125 width: i32,
126 height: i32,
127 codec_type: CMVideoCodecType,
128 encoder_specification: CFDictionary<CFString, CFType>,
129 source_image_buffer_attributes: CFDictionary<CFString, CFType>,
130 compressed_data_allocator: CFAllocator,
131 output_callback: Option<*const VTCompressionOutputCallback>,
132 output_callback_ref_con: Option<*mut c_void>,
133 ) -> Result<VTCompressionSession, OSStatus> {
134 let mut session: VTCompressionSessionRef = null_mut();
135 let status = VTCompressionSessionCreate(
136 compressed_data_allocator.as_concrete_TypeRef(),
137 width,
138 height,
139 codec_type,
140 encoder_specification.as_concrete_TypeRef(),
141 source_image_buffer_attributes.as_concrete_TypeRef(),
142 compressed_data_allocator.as_concrete_TypeRef(),
143 output_callback.unwrap_or(null()),
144 output_callback_ref_con.unwrap_or(null_mut()),
145 &mut session,
146 );
147 if status == 0 {
148 Ok(unsafe { VTCompressionSession::wrap_under_create_rule(session) })
149 } else {
150 Err(status)
151 }
152 }
153
154 pub fn invalidate(&self) {
155 unsafe { VTCompressionSessionInvalidate(self.as_concrete_TypeRef()) }
156 }
157
158 pub fn get_pixel_buffer_pool(&self) -> CVPixelBufferPool {
159 unsafe {
160 let pool_ref = VTCompressionSessionGetPixelBufferPool(self.as_concrete_TypeRef());
161 CVPixelBufferPool::wrap_under_get_rule(pool_ref)
162 }
163 }
164
165 pub fn prepare_to_encode_frames(&self) -> Result<(), OSStatus> {
166 let status = unsafe { VTCompressionSessionPrepareToEncodeFrames(self.as_concrete_TypeRef()) };
167 if status == 0 {
168 Ok(())
169 } else {
170 Err(status)
171 }
172 }
173
174 pub unsafe fn encode_frame(
175 &self,
176 image_buffer: CVImageBuffer,
177 presentation_time_stamp: CMTime,
178 duration: CMTime,
179 frame_properties: CFDictionary<CFString, CFType>,
180 source_frame_refcon: *mut c_void,
181 ) -> Result<VTEncodeInfoFlags, OSStatus> {
182 let mut info_flags_out: VTEncodeInfoFlags = VTEncodeInfoFlags::empty();
183
184 let status = VTCompressionSessionEncodeFrame(
185 self.as_concrete_TypeRef(),
186 image_buffer.as_concrete_TypeRef(),
187 presentation_time_stamp,
188 duration,
189 frame_properties.as_concrete_TypeRef(),
190 source_frame_refcon,
191 &mut info_flags_out,
192 );
193
194 if status == 0 {
195 Ok(info_flags_out)
196 } else {
197 Err(status)
198 }
199 }
200
201 pub fn encode_frame_with_closure<F>(
202 &self,
203 image_buffer: CVImageBuffer,
204 presentation_time_stamp: CMTime,
205 duration: CMTime,
206 frame_properties: CFDictionary<CFString, CFType>,
207 closure: F,
208 ) -> Result<VTEncodeInfoFlags, OSStatus>
209 where
210 F: Fn(OSStatus, VTEncodeInfoFlags, CMSampleBufferRef) + 'static,
211 {
212 let handler = ConcreteBlock::new(move |status: OSStatus, info_flags: VTEncodeInfoFlags, sample_buffer: CMSampleBufferRef| {
213 closure(status, info_flags, sample_buffer)
214 })
215 .copy();
216
217 let mut info_flags_out: VTEncodeInfoFlags = VTEncodeInfoFlags::empty();
218
219 let status = unsafe {
220 VTCompressionSessionEncodeFrameWithOutputHandler(
221 self.as_concrete_TypeRef(),
222 image_buffer.as_concrete_TypeRef(),
223 presentation_time_stamp,
224 duration,
225 frame_properties.as_concrete_TypeRef(),
226 &mut info_flags_out,
227 &*handler,
228 )
229 };
230
231 if status == 0 {
232 Ok(info_flags_out)
233 } else {
234 Err(status)
235 }
236 }
237
238 pub fn complete_frames(&self, complete_until_presentation_time_stamp: CMTime) -> Result<(), OSStatus> {
239 let status = unsafe { VTCompressionSessionCompleteFrames(self.as_concrete_TypeRef(), complete_until_presentation_time_stamp) };
240 if status == 0 {
241 Ok(())
242 } else {
243 Err(status)
244 }
245 }
246
247 pub fn begin_pass(&self, begin_pass_flags: VTCompressionSessionOptionFlags) -> Result<(), OSStatus> {
248 let status = unsafe { VTCompressionSessionBeginPass(self.as_concrete_TypeRef(), begin_pass_flags, null_mut()) };
249 if status == 0 {
250 Ok(())
251 } else {
252 Err(status)
253 }
254 }
255
256 pub fn end_pass(&self) -> Result<Boolean, OSStatus> {
257 let mut further_passes_requested_out: Boolean = 0;
258 let status = unsafe { VTCompressionSessionEndPass(self.as_concrete_TypeRef(), &mut further_passes_requested_out, null_mut()) };
259 if status == 0 {
260 Ok(further_passes_requested_out)
261 } else {
262 Err(status)
263 }
264 }
265
266 pub fn get_time_ranges_for_next_pass(&self) -> Result<Vec<CMTimeRange>, OSStatus> {
267 let mut time_range_count_out: CMItemCount = 0;
268
269 let status = unsafe { VTCompressionSessionGetTimeRangesForNextPass(self.as_concrete_TypeRef(), &mut time_range_count_out, null()) };
270 if status != 0 {
271 return Err(status);
272 }
273
274 let mut time_range_array: Vec<CMTimeRange> = Vec::with_capacity(time_range_count_out as usize);
275 let status = unsafe {
276 let result =
277 VTCompressionSessionGetTimeRangesForNextPass(self.as_concrete_TypeRef(), &mut time_range_count_out, time_range_array.as_mut_ptr());
278 if result == 0 {
279 time_range_array.set_len(time_range_count_out as usize);
280 }
281 result
282 };
283
284 if status == 0 {
285 Ok(time_range_array)
286 } else {
287 Err(status)
288 }
289 }
290}