1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{mem, ptr}; |
4 | |
5 | use glib::{prelude::*, translate::*}; |
6 | |
7 | #[cfg (feature = "v1_16" )] |
8 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
9 | use crate::VideoInterlaceMode; |
10 | use crate::{ |
11 | utils::HasStreamLock, |
12 | video_codec_state::{InNegotiation, Readable, VideoCodecState, VideoCodecStateContext}, |
13 | VideoCodecFrame, VideoDecoder, VideoFormat, |
14 | }; |
15 | |
16 | extern "C" { |
17 | fn _gst_video_decoder_error( |
18 | dec: *mut ffi::GstVideoDecoder, |
19 | weight: i32, |
20 | domain: glib::ffi::GQuark, |
21 | code: i32, |
22 | txt: *mut libc::c_char, |
23 | debug: *mut libc::c_char, |
24 | file: *const libc::c_char, |
25 | function: *const libc::c_char, |
26 | line: i32, |
27 | ) -> gst::ffi::GstFlowReturn; |
28 | } |
29 | |
30 | mod sealed { |
31 | pub trait Sealed {} |
32 | impl<T: super::IsA<super::VideoDecoder>> Sealed for T {} |
33 | } |
34 | |
35 | pub trait VideoDecoderExtManual: sealed::Sealed + IsA<VideoDecoder> + 'static { |
36 | #[doc (alias = "gst_video_decoder_allocate_output_frame" )] |
37 | fn allocate_output_frame( |
38 | &self, |
39 | frame: &mut VideoCodecFrame, |
40 | params: Option<&gst::BufferPoolAcquireParams>, |
41 | ) -> Result<gst::FlowSuccess, gst::FlowError> { |
42 | unsafe { |
43 | let params_ptr = params.to_glib_none().0 as *mut _; |
44 | try_from_glib(ffi::gst_video_decoder_allocate_output_frame_with_params( |
45 | self.as_ref().to_glib_none().0, |
46 | frame.to_glib_none().0, |
47 | params_ptr, |
48 | )) |
49 | } |
50 | } |
51 | |
52 | #[doc (alias = "get_frame" )] |
53 | #[doc (alias = "gst_video_decoder_get_frame" )] |
54 | fn frame(&self, frame_number: i32) -> Option<VideoCodecFrame> { |
55 | let frame = unsafe { |
56 | ffi::gst_video_decoder_get_frame(self.as_ref().to_glib_none().0, frame_number) |
57 | }; |
58 | |
59 | if frame.is_null() { |
60 | None |
61 | } else { |
62 | unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) } |
63 | } |
64 | } |
65 | |
66 | #[doc (alias = "get_frames" )] |
67 | #[doc (alias = "gst_video_decoder_get_frames" )] |
68 | fn frames(&self) -> Vec<VideoCodecFrame> { |
69 | unsafe { |
70 | let frames = ffi::gst_video_decoder_get_frames(self.as_ref().to_glib_none().0); |
71 | let mut iter: *const glib::ffi::GList = frames; |
72 | let mut vec = Vec::new(); |
73 | |
74 | while !iter.is_null() { |
75 | let frame_ptr = Ptr::from((*iter).data); |
76 | /* transfer ownership of the frame */ |
77 | let frame = VideoCodecFrame::new(frame_ptr, self.as_ref()); |
78 | vec.push(frame); |
79 | iter = (*iter).next; |
80 | } |
81 | |
82 | glib::ffi::g_list_free(frames); |
83 | vec |
84 | } |
85 | } |
86 | |
87 | #[doc (alias = "get_oldest_frame" )] |
88 | #[doc (alias = "gst_video_decoder_get_oldest_frame" )] |
89 | fn oldest_frame(&self) -> Option<VideoCodecFrame> { |
90 | let frame = |
91 | unsafe { ffi::gst_video_decoder_get_oldest_frame(self.as_ref().to_glib_none().0) }; |
92 | |
93 | if frame.is_null() { |
94 | None |
95 | } else { |
96 | unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) } |
97 | } |
98 | } |
99 | |
100 | #[doc (alias = "get_allocator" )] |
101 | #[doc (alias = "gst_video_decoder_get_allocator" )] |
102 | fn allocator(&self) -> (Option<gst::Allocator>, gst::AllocationParams) { |
103 | unsafe { |
104 | let mut allocator = ptr::null_mut(); |
105 | let mut params = mem::MaybeUninit::uninit(); |
106 | ffi::gst_video_decoder_get_allocator( |
107 | self.as_ref().to_glib_none().0, |
108 | &mut allocator, |
109 | params.as_mut_ptr(), |
110 | ); |
111 | (from_glib_full(allocator), params.assume_init().into()) |
112 | } |
113 | } |
114 | #[doc (alias = "get_latency" )] |
115 | #[doc (alias = "gst_video_decoder_get_latency" )] |
116 | fn latency(&self) -> (gst::ClockTime, Option<gst::ClockTime>) { |
117 | let mut min_latency = gst::ffi::GST_CLOCK_TIME_NONE; |
118 | let mut max_latency = gst::ffi::GST_CLOCK_TIME_NONE; |
119 | |
120 | unsafe { |
121 | ffi::gst_video_decoder_get_latency( |
122 | self.as_ref().to_glib_none().0, |
123 | &mut min_latency, |
124 | &mut max_latency, |
125 | ); |
126 | |
127 | ( |
128 | try_from_glib(min_latency).expect("undefined min_latency" ), |
129 | from_glib(max_latency), |
130 | ) |
131 | } |
132 | } |
133 | |
134 | #[doc (alias = "gst_video_decoder_set_latency" )] |
135 | fn set_latency( |
136 | &self, |
137 | min_latency: gst::ClockTime, |
138 | max_latency: impl Into<Option<gst::ClockTime>>, |
139 | ) { |
140 | unsafe { |
141 | ffi::gst_video_decoder_set_latency( |
142 | self.as_ref().to_glib_none().0, |
143 | min_latency.into_glib(), |
144 | max_latency.into().into_glib(), |
145 | ); |
146 | } |
147 | } |
148 | |
149 | #[doc (alias = "get_output_state" )] |
150 | #[doc (alias = "gst_video_decoder_get_output_state" )] |
151 | fn output_state(&self) -> Option<VideoCodecState<'static, Readable>> { |
152 | let state = |
153 | unsafe { ffi::gst_video_decoder_get_output_state(self.as_ref().to_glib_none().0) }; |
154 | |
155 | if state.is_null() { |
156 | None |
157 | } else { |
158 | unsafe { Some(VideoCodecState::<Readable>::new(state)) } |
159 | } |
160 | } |
161 | |
162 | #[doc (alias = "gst_video_decoder_set_output_state" )] |
163 | fn set_output_state( |
164 | &self, |
165 | fmt: VideoFormat, |
166 | width: u32, |
167 | height: u32, |
168 | reference: Option<&VideoCodecState<Readable>>, |
169 | ) -> Result<VideoCodecState<InNegotiation>, gst::FlowError> { |
170 | let state = unsafe { |
171 | let reference = match reference { |
172 | Some(reference) => reference.as_mut_ptr(), |
173 | None => ptr::null_mut(), |
174 | }; |
175 | ffi::gst_video_decoder_set_output_state( |
176 | self.as_ref().to_glib_none().0, |
177 | fmt.into_glib(), |
178 | width, |
179 | height, |
180 | reference, |
181 | ) |
182 | }; |
183 | |
184 | if state.is_null() { |
185 | Err(gst::FlowError::NotNegotiated) |
186 | } else { |
187 | unsafe { Ok(VideoCodecState::<InNegotiation>::new(state, self.as_ref())) } |
188 | } |
189 | } |
190 | |
191 | #[cfg (feature = "v1_16" )] |
192 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
193 | #[doc (alias = "gst_video_decoder_set_interlaced_output_state" )] |
194 | fn set_interlaced_output_state( |
195 | &self, |
196 | fmt: VideoFormat, |
197 | mode: VideoInterlaceMode, |
198 | width: u32, |
199 | height: u32, |
200 | reference: Option<&VideoCodecState<Readable>>, |
201 | ) -> Result<VideoCodecState<InNegotiation>, gst::FlowError> { |
202 | let state = unsafe { |
203 | let reference = match reference { |
204 | Some(reference) => reference.as_mut_ptr(), |
205 | None => ptr::null_mut(), |
206 | }; |
207 | ffi::gst_video_decoder_set_interlaced_output_state( |
208 | self.as_ref().to_glib_none().0, |
209 | fmt.into_glib(), |
210 | mode.into_glib(), |
211 | width, |
212 | height, |
213 | reference, |
214 | ) |
215 | }; |
216 | |
217 | if state.is_null() { |
218 | Err(gst::FlowError::NotNegotiated) |
219 | } else { |
220 | unsafe { Ok(VideoCodecState::<InNegotiation>::new(state, self.as_ref())) } |
221 | } |
222 | } |
223 | |
224 | #[doc (alias = "gst_video_decoder_negotiate" )] |
225 | fn negotiate<'a>( |
226 | &'a self, |
227 | output_state: VideoCodecState<'a, InNegotiation<'a>>, |
228 | ) -> Result<(), gst::FlowError> { |
229 | // Consume output_state so user won't be able to modify it anymore |
230 | let self_ptr = self.to_glib_none().0 as *const gst::ffi::GstElement; |
231 | assert_eq!(output_state.context.element_as_ptr(), self_ptr); |
232 | |
233 | let ret = unsafe { |
234 | from_glib(ffi::gst_video_decoder_negotiate( |
235 | self.as_ref().to_glib_none().0, |
236 | )) |
237 | }; |
238 | if ret { |
239 | Ok(()) |
240 | } else { |
241 | Err(gst::FlowError::NotNegotiated) |
242 | } |
243 | } |
244 | |
245 | #[allow (clippy::too_many_arguments)] |
246 | fn error<T: gst::MessageErrorDomain>( |
247 | &self, |
248 | weight: i32, |
249 | code: T, |
250 | message: Option<&str>, |
251 | debug: Option<&str>, |
252 | file: &str, |
253 | function: &str, |
254 | line: u32, |
255 | ) -> Result<gst::FlowSuccess, gst::FlowError> { |
256 | unsafe { |
257 | try_from_glib(_gst_video_decoder_error( |
258 | self.as_ref().to_glib_none().0, |
259 | weight, |
260 | T::domain().into_glib(), |
261 | code.code(), |
262 | message.to_glib_full(), |
263 | debug.to_glib_full(), |
264 | file.to_glib_none().0, |
265 | function.to_glib_none().0, |
266 | line as i32, |
267 | )) |
268 | } |
269 | } |
270 | |
271 | fn sink_pad(&self) -> &gst::Pad { |
272 | unsafe { |
273 | let elt = &*(self.as_ptr() as *const ffi::GstVideoDecoder); |
274 | &*(&elt.sinkpad as *const *mut gst::ffi::GstPad as *const gst::Pad) |
275 | } |
276 | } |
277 | |
278 | fn src_pad(&self) -> &gst::Pad { |
279 | unsafe { |
280 | let elt = &*(self.as_ptr() as *const ffi::GstVideoDecoder); |
281 | &*(&elt.srcpad as *const *mut gst::ffi::GstPad as *const gst::Pad) |
282 | } |
283 | } |
284 | } |
285 | |
286 | impl<O: IsA<VideoDecoder>> VideoDecoderExtManual for O {} |
287 | |
288 | impl HasStreamLock for VideoDecoder { |
289 | fn stream_lock(&self) -> *mut glib::ffi::GRecMutex { |
290 | let decoder_sys: *const ffi::GstVideoDecoder = self.to_glib_none().0; |
291 | unsafe { &(*decoder_sys).stream_lock as *const _ as usize as *mut _ } |
292 | } |
293 | |
294 | fn element_as_ptr(&self) -> *const gst::ffi::GstElement { |
295 | let decoder_sys: *const ffi::GstVideoDecoder = self.to_glib_none().0; |
296 | decoder_sys as *const gst::ffi::GstElement |
297 | } |
298 | } |
299 | |
300 | #[macro_export ] |
301 | macro_rules! video_decoder_error( |
302 | ($obj:expr, $weight:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*]) => { { |
303 | use $crate::prelude::VideoDecoderExtManual; |
304 | $obj.error( |
305 | $weight, |
306 | $err, |
307 | Some(&format!($($msg)*)), |
308 | Some(&format!($($debug)*)), |
309 | file!(), |
310 | $crate::glib::function_name!(), |
311 | line!(), |
312 | ) |
313 | }}; |
314 | ($obj:expr, $weight:expr, $err:expr, ($($msg:tt)*)) => { { |
315 | use $crate::prelude::VideoDecoderExtManual; |
316 | $obj.error( |
317 | $weight, |
318 | $err, |
319 | Some(&format!($($msg)*)), |
320 | None, |
321 | file!(), |
322 | $crate::glib::function_name!(), |
323 | line!(), |
324 | ) |
325 | }}; |
326 | ($obj:expr, $weight:expr, $err:expr, [$($debug:tt)*]) => { { |
327 | use $crate::prelude::VideoDecoderExtManual; |
328 | $obj.error( |
329 | $weight, |
330 | $err, |
331 | None, |
332 | Some(&format!($($debug)*)), |
333 | file!(), |
334 | $crate::glib::function_name!(), |
335 | line!(), |
336 | ) |
337 | }}; |
338 | ); |
339 | |