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