Skip to main content

video_toolbox/
decompression_session.rs

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