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}