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    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}