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}