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    /// If multiple subframes are produced for one input frame then use this method
139    /// for each subframe, except for the last one. Before calling this function,
140    /// you need to fill frame->output_buffer with the encoded buffer to push.
141    ///
142    /// You must call [`VideoEncoderExt::finish_frame()`][crate::prelude::VideoEncoderExt::finish_frame()] for the last sub-frame
143    /// to tell the encoder that the frame has been fully encoded.
144    ///
145    /// This function will change the metadata of `frame` and frame->output_buffer
146    /// will be pushed downstream.
147    /// ## `frame`
148    /// a [`VideoCodecFrame`][crate::VideoCodecFrame] being encoded
149    ///
150    /// # Returns
151    ///
152    /// a [`gst::FlowReturn`][crate::gst::FlowReturn] resulting from pushing the buffer downstream.
153    #[cfg(feature = "v1_18")]
154    #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
155    #[doc(alias = "gst_video_encoder_finish_subframe")]
156    fn finish_subframe(&self, frame: &VideoCodecFrame) -> Result<gst::FlowSuccess, gst::FlowError> {
157        unsafe {
158            try_from_glib(ffi::gst_video_encoder_finish_subframe(
159                self.as_ref().to_glib_none().0,
160                frame.to_glib_none().0,
161            ))
162        }
163    }
164
165    /// Query the configured encoding latency. Results will be returned via
166    /// `min_latency` and `max_latency`.
167    ///
168    /// # Returns
169    ///
170    ///
171    /// ## `min_latency`
172    /// address of variable in which to store the
173    ///  configured minimum latency, or [`None`]
174    ///
175    /// ## `max_latency`
176    /// address of variable in which to store the
177    ///  configured maximum latency, or [`None`]
178    #[doc(alias = "get_latency")]
179    #[doc(alias = "gst_video_encoder_get_latency")]
180    fn latency(&self) -> (gst::ClockTime, Option<gst::ClockTime>) {
181        let mut min_latency = gst::ffi::GST_CLOCK_TIME_NONE;
182        let mut max_latency = gst::ffi::GST_CLOCK_TIME_NONE;
183
184        unsafe {
185            ffi::gst_video_encoder_get_latency(
186                self.as_ref().to_glib_none().0,
187                &mut min_latency,
188                &mut max_latency,
189            );
190
191            (
192                try_from_glib(min_latency).expect("undefined min_latency"),
193                from_glib(max_latency),
194            )
195        }
196    }
197
198    /// Informs baseclass of encoding latency. If the provided values changed from
199    /// previously provided ones, this will also post a LATENCY message on the bus
200    /// so the pipeline can reconfigure its global latency.
201    /// ## `min_latency`
202    /// minimum latency
203    /// ## `max_latency`
204    /// maximum latency
205    #[doc(alias = "gst_video_encoder_set_latency")]
206    fn set_latency(
207        &self,
208        min_latency: gst::ClockTime,
209        max_latency: impl Into<Option<gst::ClockTime>>,
210    ) {
211        unsafe {
212            ffi::gst_video_encoder_set_latency(
213                self.as_ref().to_glib_none().0,
214                min_latency.into_glib(),
215                max_latency.into().into_glib(),
216            );
217        }
218    }
219    /// Get the current [`VideoCodecState`][crate::VideoCodecState]
220    ///
221    /// # Returns
222    ///
223    /// [`VideoCodecState`][crate::VideoCodecState] describing format of video data.
224    #[doc(alias = "get_output_state")]
225    #[doc(alias = "gst_video_encoder_get_output_state")]
226    fn output_state(&self) -> Option<VideoCodecState<'static, Readable>> {
227        let state =
228            unsafe { ffi::gst_video_encoder_get_output_state(self.as_ref().to_glib_none().0) };
229
230        if state.is_null() {
231            None
232        } else {
233            unsafe { Some(VideoCodecState::<Readable>::new(state)) }
234        }
235    }
236
237    /// Creates a new [`VideoCodecState`][crate::VideoCodecState] with the specified caps as the output state
238    /// for the encoder.
239    /// Any previously set output state on `self` will be replaced by the newly
240    /// created one.
241    ///
242    /// The specified `caps` should not contain any resolution, pixel-aspect-ratio,
243    /// framerate, codec-data, .... Those should be specified instead in the returned
244    /// [`VideoCodecState`][crate::VideoCodecState].
245    ///
246    /// If the subclass wishes to copy over existing fields (like pixel aspect ratio,
247    /// or framerate) from an existing [`VideoCodecState`][crate::VideoCodecState], it can be provided as a
248    /// `reference`.
249    ///
250    /// If the subclass wishes to override some fields from the output state (like
251    /// pixel-aspect-ratio or framerate) it can do so on the returned [`VideoCodecState`][crate::VideoCodecState].
252    ///
253    /// The new output state will only take effect (set on pads and buffers) starting
254    /// from the next call to [`VideoEncoderExt::finish_frame()`][crate::prelude::VideoEncoderExt::finish_frame()].
255    /// ## `caps`
256    /// the [`gst::Caps`][crate::gst::Caps] to use for the output
257    /// ## `reference`
258    /// An optional reference [`VideoCodecState`][crate::VideoCodecState]
259    ///
260    /// # Returns
261    ///
262    /// the newly configured output state.
263    #[doc(alias = "gst_video_encoder_set_output_state")]
264    fn set_output_state(
265        &self,
266        caps: gst::Caps,
267        reference: Option<&VideoCodecState<Readable>>,
268    ) -> Result<VideoCodecState<'_, InNegotiation<'_>>, gst::FlowError> {
269        let state = unsafe {
270            let reference = match reference {
271                Some(reference) => reference.as_mut_ptr(),
272                None => ptr::null_mut(),
273            };
274            ffi::gst_video_encoder_set_output_state(
275                self.as_ref().to_glib_none().0,
276                caps.into_glib_ptr(),
277                reference,
278            )
279        };
280
281        if state.is_null() {
282            Err(gst::FlowError::NotNegotiated)
283        } else {
284            unsafe { Ok(VideoCodecState::<InNegotiation>::new(state, self.as_ref())) }
285        }
286    }
287
288    /// Negotiate with downstream elements to currently configured [`VideoCodecState`][crate::VideoCodecState].
289    /// Unmark GST_PAD_FLAG_NEED_RECONFIGURE in any case. But mark it again if
290    /// negotiate fails.
291    ///
292    /// # Returns
293    ///
294    /// [`true`] if the negotiation succeeded, else [`false`].
295    #[doc(alias = "gst_video_encoder_negotiate")]
296    fn negotiate<'a>(
297        &'a self,
298        output_state: VideoCodecState<'a, InNegotiation<'a>>,
299    ) -> Result<(), gst::FlowError> {
300        // Consume output_state so user won't be able to modify it anymore
301        let self_ptr = self.to_glib_none().0 as *const gst::ffi::GstElement;
302        assert_eq!(output_state.context.element_as_ptr(), self_ptr);
303
304        let ret = unsafe {
305            from_glib(ffi::gst_video_encoder_negotiate(
306                self.as_ref().to_glib_none().0,
307            ))
308        };
309        if ret {
310            Ok(())
311        } else {
312            Err(gst::FlowError::NotNegotiated)
313        }
314    }
315
316    /// Set the codec headers to be sent downstream whenever requested.
317    /// ## `headers`
318    /// a list of [`gst::Buffer`][crate::gst::Buffer] containing the codec header
319    #[doc(alias = "gst_video_encoder_set_headers")]
320    fn set_headers(&self, headers: impl IntoIterator<Item = gst::Buffer>) {
321        unsafe {
322            ffi::gst_video_encoder_set_headers(
323                self.as_ref().to_glib_none().0,
324                headers
325                    .into_iter()
326                    .collect::<glib::List<_>>()
327                    .into_glib_ptr(),
328            );
329        }
330    }
331
332    fn sink_pad(&self) -> &gst::Pad {
333        unsafe {
334            let elt = &*(self.as_ptr() as *const ffi::GstVideoEncoder);
335            &*(&elt.sinkpad as *const *mut gst::ffi::GstPad as *const gst::Pad)
336        }
337    }
338
339    fn src_pad(&self) -> &gst::Pad {
340        unsafe {
341            let elt = &*(self.as_ptr() as *const ffi::GstVideoEncoder);
342            &*(&elt.srcpad as *const *mut gst::ffi::GstPad as *const gst::Pad)
343        }
344    }
345
346    fn input_segment(&self) -> gst::Segment {
347        unsafe {
348            let ptr: &ffi::GstVideoDecoder = &*(self.as_ptr() as *const _);
349            glib::ffi::g_rec_mutex_lock(mut_override(&ptr.stream_lock));
350            let segment = ptr.input_segment;
351            glib::ffi::g_rec_mutex_unlock(mut_override(&ptr.stream_lock));
352            from_glib_none(&segment as *const gst::ffi::GstSegment)
353        }
354    }
355
356    fn output_segment(&self) -> gst::Segment {
357        unsafe {
358            let ptr: &ffi::GstVideoDecoder = &*(self.as_ptr() as *const _);
359            glib::ffi::g_rec_mutex_lock(mut_override(&ptr.stream_lock));
360            let segment = ptr.output_segment;
361            glib::ffi::g_rec_mutex_unlock(mut_override(&ptr.stream_lock));
362            from_glib_none(&segment as *const gst::ffi::GstSegment)
363        }
364    }
365}
366
367impl<O: IsA<VideoEncoder>> VideoEncoderExtManual for O {}
368
369impl HasStreamLock for VideoEncoder {
370    fn stream_lock(&self) -> *mut glib::ffi::GRecMutex {
371        let encoder_sys: *const ffi::GstVideoEncoder = self.to_glib_none().0;
372        unsafe { mut_override(&(*encoder_sys).stream_lock) }
373    }
374
375    fn element_as_ptr(&self) -> *const gst::ffi::GstElement {
376        self.as_ptr() as *const gst::ffi::GstElement
377    }
378}