1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{mem, ptr};
4
5use glib::{prelude::*, translate::*};
6
7#[cfg(feature = "v1_16")]
8#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
9use crate::VideoInterlaceMode;
10use crate::{
11 utils::HasStreamLock,
12 video_codec_state::{InNegotiation, Readable, VideoCodecState, VideoCodecStateContext},
13 VideoCodecFrame, VideoDecoder, VideoFormat,
14};
15
16extern "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
30mod sealed {
31 pub trait Sealed {}
32 impl<T: super::IsA<super::VideoDecoder>> Sealed for T {}
33}
34
35pub 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
286impl<O: IsA<VideoDecoder>> VideoDecoderExtManual for O {}
287
288impl 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]
301macro_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