Skip to main content

video_toolbox/
compression_session.rs

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}