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