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}