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