Skip to main content

gstreamer_video/
video_encoder.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
7use crate::{
8    VideoCodecFrame, VideoEncoder, ffi,
9    utils::HasStreamLock,
10    video_codec_state::{InNegotiation, Readable, VideoCodecState, VideoCodecStateContext},
11};
12
13pub trait VideoEncoderExtManual: IsA<VideoEncoder> + 'static {
14    /// Helper function that allocates a buffer to hold an encoded video frame for `self`'s
15    /// current [`VideoCodecState`][crate::VideoCodecState]. Subclass should already have configured video
16    /// state and set src pad caps.
17    ///
18    /// The buffer allocated here is owned by the frame and you should only
19    /// keep references to the frame, not the buffer.
20    /// ## `frame`
21    /// a [`VideoCodecFrame`][crate::VideoCodecFrame]
22    /// ## `size`
23    /// size of the buffer
24    ///
25    /// # Returns
26    ///
27    /// [`gst::FlowReturn::Ok`][crate::gst::FlowReturn::Ok] if an output buffer could be allocated
28    #[doc(alias = "gst_video_encoder_allocate_output_frame")]
29    fn allocate_output_frame(
30        &self,
31        frame: &mut VideoCodecFrame,
32        size: usize,
33    ) -> Result<gst::FlowSuccess, gst::FlowError> {
34        unsafe {
35            try_from_glib(ffi::gst_video_encoder_allocate_output_frame(
36                self.as_ref().to_glib_none().0,
37                frame.to_glib_none().0,
38                size,
39            ))
40        }
41    }
42
43    /// Get a pending unfinished [`VideoCodecFrame`][crate::VideoCodecFrame]
44    /// ## `frame_number`
45    /// system_frame_number of a frame
46    ///
47    /// # Returns
48    ///
49    /// pending unfinished [`VideoCodecFrame`][crate::VideoCodecFrame] identified by `frame_number`.
50    #[doc(alias = "get_frame")]
51    #[doc(alias = "gst_video_encoder_get_frame")]
52    fn frame(&self, frame_number: i32) -> Option<VideoCodecFrame<'_>> {
53        let frame = unsafe {
54            ffi::gst_video_encoder_get_frame(self.as_ref().to_glib_none().0, frame_number)
55        };
56
57        if frame.is_null() {
58            None
59        } else {
60            unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) }
61        }
62    }
63
64    /// Get all pending unfinished [`VideoCodecFrame`][crate::VideoCodecFrame]
65    ///
66    /// # Returns
67    ///
68    /// pending unfinished [`VideoCodecFrame`][crate::VideoCodecFrame].
69    #[doc(alias = "get_frames")]
70    #[doc(alias = "gst_video_encoder_get_frames")]
71    fn frames(&self) -> Vec<VideoCodecFrame<'_>> {
72        unsafe {
73            let frames = ffi::gst_video_encoder_get_frames(self.as_ref().to_glib_none().0);
74            let mut iter: *const glib::ffi::GList = frames;
75            let mut vec = Vec::new();
76
77            while !iter.is_null() {
78                let frame_ptr = Ptr::from((*iter).data);
79                /* transfer ownership of the frame */
80                let frame = VideoCodecFrame::new(frame_ptr, self.as_ref());
81                vec.push(frame);
82                iter = (*iter).next;
83            }
84
85            glib::ffi::g_list_free(frames);
86            vec
87        }
88    }
89
90    /// Get the oldest unfinished pending [`VideoCodecFrame`][crate::VideoCodecFrame]
91    ///
92    /// # Returns
93    ///
94    /// oldest unfinished pending [`VideoCodecFrame`][crate::VideoCodecFrame]
95    #[doc(alias = "get_oldest_frame")]
96    #[doc(alias = "gst_video_encoder_get_oldest_frame")]
97    fn oldest_frame(&self) -> Option<VideoCodecFrame<'_>> {
98        let frame =
99            unsafe { ffi::gst_video_encoder_get_oldest_frame(self.as_ref().to_glib_none().0) };
100
101        if frame.is_null() {
102            None
103        } else {
104            unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) }
105        }
106    }
107
108    /// Lets [`VideoEncoder`][crate::VideoEncoder] sub-classes to know the memory `allocator`
109    /// used by the base class and its `params`.
110    ///
111    /// Unref the `allocator` after use it.
112    ///
113    /// # Returns
114    ///
115    ///
116    /// ## `allocator`
117    /// the [`gst::Allocator`][crate::gst::Allocator]
118    /// used
119    ///
120    /// ## `params`
121    /// the
122    /// [`gst::AllocationParams`][crate::gst::AllocationParams] of `allocator`
123    #[doc(alias = "get_allocator")]
124    #[doc(alias = "gst_video_encoder_get_allocator")]
125    fn allocator(&self) -> (Option<gst::Allocator>, gst::AllocationParams) {
126        unsafe {
127            let mut allocator = ptr::null_mut();
128            let mut params = mem::MaybeUninit::uninit();
129            ffi::gst_video_encoder_get_allocator(
130                self.as_ref().to_glib_none().0,
131                &mut allocator,
132                params.as_mut_ptr(),
133            );
134            (from_glib_full(allocator), params.assume_init().into())
135        }
136    }
137
138    /// output_buffer
139    /// will be pushed downstream.
140    /// ## `frame`
141    /// a [`VideoCodecFrame`][crate::VideoCodecFrame] being encoded
142    ///
143    /// # Returns
144    ///
145    /// a [`gst::FlowReturn`][crate::gst::FlowReturn] resulting from pushing the buffer downstream.
146    #[cfg(feature = "v1_18")]
147    #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
148    #[doc(alias = "gst_video_encoder_finish_subframe")]
149    fn finish_subframe(&self, frame: &VideoCodecFrame) -> Result<gst::FlowSuccess, gst::FlowError> {
150        unsafe {
151            try_from_glib(ffi::gst_video_encoder_finish_subframe(
152                self.as_ref().to_glib_none().0,
153                frame.to_glib_none().0,
154            ))
155        }
156    }
157
158    /// Query the configured encoding latency. Results will be returned via
159    /// `min_latency` and `max_latency`.
160    ///
161    /// # Returns
162    ///
163    ///
164    /// ## `min_latency`
165    /// address of variable in which to store the
166    ///  configured minimum latency, or [`None`]
167    ///
168    /// ## `max_latency`
169    /// address of variable in which to store the
170    ///  configured maximum latency, or [`None`]
171    #[doc(alias = "get_latency")]
172    #[doc(alias = "gst_video_encoder_get_latency")]
173    fn latency(&self) -> (gst::ClockTime, Option<gst::ClockTime>) {
174        let mut min_latency = gst::ffi::GST_CLOCK_TIME_NONE;
175        let mut max_latency = gst::ffi::GST_CLOCK_TIME_NONE;
176
177        unsafe {
178            ffi::gst_video_encoder_get_latency(
179                self.as_ref().to_glib_none().0,
180                &mut min_latency,
181                &mut max_latency,
182            );
183
184            (
185                try_from_glib(min_latency).expect("undefined min_latency"),
186                from_glib(max_latency),
187            )
188        }
189    }
190
191    /// Informs baseclass of encoding latency. If the provided values changed from
192    /// previously provided ones, this will also post a LATENCY message on the bus
193    /// so the pipeline can reconfigure its global latency.
194    /// ## `min_latency`
195    /// minimum latency
196    /// ## `max_latency`
197    /// maximum latency
198    #[doc(alias = "gst_video_encoder_set_latency")]
199    fn set_latency(
200        &self,
201        min_latency: gst::ClockTime,
202        max_latency: impl Into<Option<gst::ClockTime>>,
203    ) {
204        unsafe {
205            ffi::gst_video_encoder_set_latency(
206                self.as_ref().to_glib_none().0,
207                min_latency.into_glib(),
208                max_latency.into().into_glib(),
209            );
210        }
211    }
212    /// Get the current [`VideoCodecState`][crate::VideoCodecState]
213    ///
214    /// # Returns
215    ///
216    /// [`VideoCodecState`][crate::VideoCodecState] describing format of video data.
217    #[doc(alias = "get_output_state")]
218    #[doc(alias = "gst_video_encoder_get_output_state")]
219    fn output_state(&self) -> Option<VideoCodecState<'static, Readable>> {
220        let state =
221            unsafe { ffi::gst_video_encoder_get_output_state(self.as_ref().to_glib_none().0) };
222
223        if state.is_null() {
224            None
225        } else {
226            unsafe { Some(VideoCodecState::<Readable>::new(state)) }
227        }
228    }
229
230    /// Creates a new [`VideoCodecState`][crate::VideoCodecState] with the specified caps as the output state
231    /// for the encoder.
232    /// Any previously set output state on `self` will be replaced by the newly
233    /// created one.
234    ///
235    /// The specified `caps` should not contain any resolution, pixel-aspect-ratio,
236    /// framerate, codec-data, .... Those should be specified instead in the returned
237    /// [`VideoCodecState`][crate::VideoCodecState].
238    ///
239    /// If the subclass wishes to copy over existing fields (like pixel aspect ratio,
240    /// or framerate) from an existing [`VideoCodecState`][crate::VideoCodecState], it can be provided as a
241    /// `reference`.
242    ///
243    /// If the subclass wishes to override some fields from the output state (like
244    /// pixel-aspect-ratio or framerate) it can do so on the returned [`VideoCodecState`][crate::VideoCodecState].
245    ///
246    /// The new output state will only take effect (set on pads and buffers) starting
247    /// from the next call to [`VideoEncoderExt::finish_frame()`][crate::prelude::VideoEncoderExt::finish_frame()].
248    /// ## `caps`
249    /// the [`gst::Caps`][crate::gst::Caps] to use for the output
250    /// ## `reference`
251    /// An optional reference [`VideoCodecState`][crate::VideoCodecState]
252    ///
253    /// # Returns
254    ///
255    /// the newly configured output state.
256    #[doc(alias = "gst_video_encoder_set_output_state")]
257    fn set_output_state(
258        &self,
259        caps: gst::Caps,
260        reference: Option<&VideoCodecState<Readable>>,
261    ) -> Result<VideoCodecState<'_, InNegotiation<'_>>, gst::FlowError> {
262        let state = unsafe {
263            let reference = match reference {
264                Some(reference) => reference.as_mut_ptr(),
265                None => ptr::null_mut(),
266            };
267            ffi::gst_video_encoder_set_output_state(
268                self.as_ref().to_glib_none().0,
269                caps.into_glib_ptr(),
270                reference,
271            )
272        };
273
274        if state.is_null() {
275            Err(gst::FlowError::NotNegotiated)
276        } else {
277            unsafe { Ok(VideoCodecState::<InNegotiation>::new(state, self.as_ref())) }
278        }
279    }
280
281    /// Negotiate with downstream elements to currently configured [`VideoCodecState`][crate::VideoCodecState].
282    /// Unmark GST_PAD_FLAG_NEED_RECONFIGURE in any case. But mark it again if
283    /// negotiate fails.
284    ///
285    /// # Returns
286    ///
287    /// [`true`] if the negotiation succeeded, else [`false`].
288    #[doc(alias = "gst_video_encoder_negotiate")]
289    fn negotiate<'a>(
290        &'a self,
291        output_state: VideoCodecState<'a, InNegotiation<'a>>,
292    ) -> Result<(), gst::FlowError> {
293        // Consume output_state so user won't be able to modify it anymore
294        let self_ptr = self.to_glib_none().0 as *const gst::ffi::GstElement;
295        assert_eq!(output_state.context.element_as_ptr(), self_ptr);
296
297        let ret = unsafe {
298            from_glib(ffi::gst_video_encoder_negotiate(
299                self.as_ref().to_glib_none().0,
300            ))
301        };
302        if ret {
303            Ok(())
304        } else {
305            Err(gst::FlowError::NotNegotiated)
306        }
307    }
308
309    /// Set the codec headers to be sent downstream whenever requested.
310    /// ## `headers`
311    /// a list of [`gst::Buffer`][crate::gst::Buffer] containing the codec header
312    #[doc(alias = "gst_video_encoder_set_headers")]
313    fn set_headers(&self, headers: impl IntoIterator<Item = gst::Buffer>) {
314        unsafe {
315            ffi::gst_video_encoder_set_headers(
316                self.as_ref().to_glib_none().0,
317                headers
318                    .into_iter()
319                    .collect::<glib::List<_>>()
320                    .into_glib_ptr(),
321            );
322        }
323    }
324
325    fn sink_pad(&self) -> &gst::Pad {
326        unsafe {
327            let elt = &*(self.as_ptr() as *const ffi::GstVideoEncoder);
328            &*(&elt.sinkpad as *const *mut gst::ffi::GstPad as *const gst::Pad)
329        }
330    }
331
332    fn src_pad(&self) -> &gst::Pad {
333        unsafe {
334            let elt = &*(self.as_ptr() as *const ffi::GstVideoEncoder);
335            &*(&elt.srcpad as *const *mut gst::ffi::GstPad as *const gst::Pad)
336        }
337    }
338
339    fn input_segment(&self) -> gst::Segment {
340        unsafe {
341            let ptr: &ffi::GstVideoDecoder = &*(self.as_ptr() as *const _);
342            glib::ffi::g_rec_mutex_lock(mut_override(&ptr.stream_lock));
343            let segment = ptr.input_segment;
344            glib::ffi::g_rec_mutex_unlock(mut_override(&ptr.stream_lock));
345            from_glib_none(&segment as *const gst::ffi::GstSegment)
346        }
347    }
348
349    fn output_segment(&self) -> gst::Segment {
350        unsafe {
351            let ptr: &ffi::GstVideoDecoder = &*(self.as_ptr() as *const _);
352            glib::ffi::g_rec_mutex_lock(mut_override(&ptr.stream_lock));
353            let segment = ptr.output_segment;
354            glib::ffi::g_rec_mutex_unlock(mut_override(&ptr.stream_lock));
355            from_glib_none(&segment as *const gst::ffi::GstSegment)
356        }
357    }
358}
359
360impl<O: IsA<VideoEncoder>> VideoEncoderExtManual for O {}
361
362impl HasStreamLock for VideoEncoder {
363    fn stream_lock(&self) -> *mut glib::ffi::GRecMutex {
364        let encoder_sys: *const ffi::GstVideoEncoder = self.to_glib_none().0;
365        unsafe { mut_override(&(*encoder_sys).stream_lock) }
366    }
367
368    fn element_as_ptr(&self) -> *const gst::ffi::GstElement {
369        self.as_ptr() as *const gst::ffi::GstElement
370    }
371}