gstreamer_video/
video_decoder.rs

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
17extern "C" {
18    fn _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);