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}