gstreamer_check/harness.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{mem, ops, path, ptr};
4
5use glib::translate::*;
6use gst::prelude::*;
7
8use crate::{ffi, TestClock};
9
10/// [`Harness`][crate::Harness] is meant to make writing unit test for GStreamer much easier.
11/// It can be thought of as a way of treating a [`gst::Element`][crate::gst::Element] as a black box,
12/// deterministically feeding it data, and controlling what data it outputs.
13///
14/// The basic structure of [`Harness`][crate::Harness] is two "floating" `GstPads` that connect
15/// to the harnessed [`gst::Element`][crate::gst::Element] src and sink `GstPads` like so:
16///
17///
18///
19/// **⚠️ The following code is in C ⚠️**
20///
21/// ```C
22/// #include <gst/gst.h>
23/// #include <gst/check/gstharness.h>
24/// GstHarness *h;
25/// GstBuffer *in_buf;
26/// GstBuffer *out_buf;
27///
28/// // attach the harness to the src and sink pad of GstQueue
29/// h = gst_harness_new ("queue");
30///
31/// // we must specify a caps before pushing buffers
32/// gst_harness_set_src_caps_str (h, "mycaps");
33///
34/// // create a buffer of size 42
35/// in_buf = gst_harness_create_buffer (h, 42);
36///
37/// // push the buffer into the queue
38/// gst_harness_push (h, in_buf);
39///
40/// // pull the buffer from the queue
41/// out_buf = gst_harness_pull (h);
42///
43/// // validate the buffer in is the same as buffer out
44/// fail_unless (in_buf == out_buf);
45///
46/// // cleanup
47/// gst_buffer_unref (out_buf);
48/// gst_harness_teardown (h);
49///
50/// ]|
51///
52/// Another main feature of the #GstHarness is its integration with the
53/// #GstTestClock. Operating the #GstTestClock can be very challenging, but
54/// #GstHarness simplifies some of the most desired actions a lot, like wanting
55/// to manually advance the clock while at the same time releasing a #GstClockID
56/// that is waiting, with functions like gst_harness_crank_single_clock_wait().
57///
58/// #GstHarness also supports sub-harnesses, as a way of generating and
59/// validating data. A sub-harness is another #GstHarness that is managed by
60/// the "parent" harness, and can either be created by using the standard
61/// gst_harness_new type functions directly on the (GstHarness *)->src_harness,
62/// or using the much more convenient gst_harness_add_src() or
63/// gst_harness_add_sink_parse(). If you have a decoder-element you want to test,
64/// (like vp8dec) it can be very useful to add a src-harness with both a
65/// src-element (videotestsrc) and an encoder (vp8enc) to feed the decoder data
66/// with different configurations, by simply doing:
67///
68/// |[<!-- language="C" -->
69/// GstHarness * h = gst_harness_new ("vp8dec");
70/// gst_harness_add_src_parse (h, "videotestsrc is-live=1 ! vp8enc", TRUE);
71/// ```
72///
73/// and then feeding it data with:
74///
75///
76///
77/// **⚠️ The following code is in C ⚠️**
78///
79/// ```C
80/// gst_harness_push_from_src (h);
81/// ```
82#[derive(Debug)]
83#[doc(alias = "GstHarness")]
84#[repr(transparent)]
85pub struct Harness(ptr::NonNull<ffi::GstHarness>);
86
87impl Drop for Harness {
88 #[inline]
89 fn drop(&mut self) {
90 unsafe {
91 ffi::gst_harness_teardown(self.0.as_ptr());
92 }
93 }
94}
95
96unsafe impl Send for Harness {}
97unsafe impl Sync for Harness {}
98
99impl Harness {
100 /// Adds a [`gst::Element`][crate::gst::Element] to an empty [`Harness`][crate::Harness]
101 ///
102 /// MT safe.
103 /// ## `element`
104 /// a [`gst::Element`][crate::gst::Element] to add to the harness (transfer none)
105 /// ## `hsrc`
106 /// a [`gst::StaticPadTemplate`][crate::gst::StaticPadTemplate] describing the harness srcpad.
107 /// [`None`] will not create a harness srcpad.
108 /// ## `element_sinkpad_name`
109 /// a `gchar` with the name of the element
110 /// sinkpad that is then linked to the harness srcpad. Can be a static or request
111 /// or a sometimes pad that has been added. [`None`] will not get/request a sinkpad
112 /// from the element. (Like if the element is a src.)
113 /// ## `hsink`
114 /// a [`gst::StaticPadTemplate`][crate::gst::StaticPadTemplate] describing the harness sinkpad.
115 /// [`None`] will not create a harness sinkpad.
116 /// ## `element_srcpad_name`
117 /// a `gchar` with the name of the element
118 /// srcpad that is then linked to the harness sinkpad, similar to the
119 /// `element_sinkpad_name`.
120 #[doc(alias = "gst_harness_add_element_full")]
121 pub fn add_element_full<P: IsA<gst::Element>>(
122 &mut self,
123 element: &P,
124 hsrc: Option<&gst::StaticPadTemplate>,
125 element_sinkpad_name: Option<&str>,
126 hsink: Option<&gst::StaticPadTemplate>,
127 element_srcpad_name: Option<&str>,
128 ) {
129 let element_sinkpad_name = element_sinkpad_name.to_glib_none();
130 let element_srcpad_name = element_srcpad_name.to_glib_none();
131 unsafe {
132 ffi::gst_harness_add_element_full(
133 self.0.as_ptr(),
134 element.as_ref().to_glib_none().0,
135 hsrc.to_glib_none().0 as *mut _,
136 element_sinkpad_name.0,
137 hsink.to_glib_none().0 as *mut _,
138 element_srcpad_name.0,
139 );
140 }
141 }
142
143 /// Links the specified [`gst::Pad`][crate::gst::Pad] the [`Harness`][crate::Harness] srcpad.
144 ///
145 /// MT safe.
146 /// ## `sinkpad`
147 /// a [`gst::Pad`][crate::gst::Pad] to link to the harness srcpad
148 #[doc(alias = "gst_harness_add_element_sink_pad")]
149 pub fn add_element_sink_pad<P: IsA<gst::Pad>>(&mut self, sinkpad: &P) {
150 unsafe {
151 ffi::gst_harness_add_element_sink_pad(
152 self.0.as_ptr(),
153 sinkpad.as_ref().to_glib_none().0,
154 );
155 }
156 }
157
158 /// Links the specified [`gst::Pad`][crate::gst::Pad] the [`Harness`][crate::Harness] sinkpad. This can be useful if
159 /// perhaps the srcpad did not exist at the time of creating the harness,
160 /// like a demuxer that provides a sometimes-pad after receiving data.
161 ///
162 /// MT safe.
163 /// ## `srcpad`
164 /// a [`gst::Pad`][crate::gst::Pad] to link to the harness sinkpad
165 #[doc(alias = "gst_harness_add_element_src_pad")]
166 pub fn add_element_src_pad<P: IsA<gst::Pad>>(&mut self, srcpad: &P) {
167 unsafe {
168 ffi::gst_harness_add_element_src_pad(self.0.as_ptr(), srcpad.as_ref().to_glib_none().0);
169 }
170 }
171
172 /// Parses the `launchline` and puts that in a [`gst::Bin`][crate::gst::Bin],
173 /// and then attches the supplied [`Harness`][crate::Harness] to the bin.
174 ///
175 /// MT safe.
176 /// ## `launchline`
177 /// a `gchar` describing a gst-launch type line
178 #[doc(alias = "gst_harness_add_parse")]
179 pub fn add_parse(&mut self, launchline: &str) {
180 unsafe {
181 ffi::gst_harness_add_parse(self.0.as_ptr(), launchline.to_glib_none().0);
182 }
183 }
184
185 /// A convenience function to allows you to call gst_pad_add_probe on a
186 /// [`gst::Pad`][crate::gst::Pad] of a [`gst::Element`][crate::gst::Element] that are residing inside the [`Harness`][crate::Harness],
187 /// by using normal gst_pad_add_probe syntax
188 ///
189 /// MT safe.
190 /// ## `element_name`
191 /// a `gchar` with a [`gst::ElementFactory`][crate::gst::ElementFactory] name
192 /// ## `pad_name`
193 /// a `gchar` with the name of the pad to attach the probe to
194 /// ## `mask`
195 /// a [`gst::PadProbeType`][crate::gst::PadProbeType] (see gst_pad_add_probe)
196 /// ## `callback`
197 /// a `GstPadProbeCallback` (see gst_pad_add_probe)
198 /// ## `destroy_data`
199 /// a `GDestroyNotify` (see gst_pad_add_probe)
200 pub fn add_probe<F>(
201 &mut self,
202 element_name: &str,
203 pad_name: &str,
204 mask: gst::PadProbeType,
205 func: F,
206 ) where
207 F: Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static,
208 {
209 // Reimplementation of the C code so we don't have to duplicate all the callback code
210
211 let element = self.find_element(element_name).expect("Element not found");
212 let pad = element.static_pad(pad_name).expect("Pad not found");
213 pad.add_probe(mask, func);
214 }
215
216 /// Add api with params as one of the supported metadata API to propose when
217 /// receiving an allocation query.
218 ///
219 /// MT safe.
220 /// ## `api`
221 /// a metadata API
222 /// ## `params`
223 /// API specific parameters
224 #[cfg(feature = "v1_16")]
225 #[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
226 #[doc(alias = "gst_harness_add_propose_allocation_meta")]
227 pub fn add_propose_allocation_meta(
228 &mut self,
229 api: glib::types::Type,
230 params: Option<&gst::StructureRef>,
231 ) {
232 unsafe {
233 let params = params.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
234 ffi::gst_harness_add_propose_allocation_meta(self.0.as_ptr(), api.into_glib(), params);
235 }
236 }
237
238 /// Similar to gst_harness_add_sink_harness, this is a convenience to
239 /// directly create a sink-harness using the `sink_element_name` name specified.
240 ///
241 /// MT safe.
242 /// ## `sink_element_name`
243 /// a `gchar` with the name of a [`gst::Element`][crate::gst::Element]
244 #[doc(alias = "gst_harness_add_sink")]
245 pub fn add_sink(&mut self, sink_element_name: &str) {
246 unsafe {
247 ffi::gst_harness_add_sink(self.0.as_ptr(), sink_element_name.to_glib_none().0);
248 }
249 }
250
251 /// Similar to gst_harness_add_src, this allows you to send the data coming out
252 /// of your harnessed [`gst::Element`][crate::gst::Element] to a sink-element, allowing to test different
253 /// responses the element output might create in sink elements. An example might
254 /// be an existing sink providing some analytical data on the input it receives that
255 /// can be useful to your testing. If the goal is to test a sink-element itself,
256 /// this is better achieved using gst_harness_new directly on the sink.
257 ///
258 /// If a sink-harness already exists it will be replaced.
259 ///
260 /// MT safe.
261 /// ## `sink_harness`
262 /// a [`Harness`][crate::Harness] to be added as a sink-harness.
263 #[doc(alias = "gst_harness_add_sink_harness")]
264 pub fn add_sink_harness(&mut self, sink_harness: Harness) {
265 unsafe {
266 let sink_harness = mem::ManuallyDrop::new(sink_harness);
267 ffi::gst_harness_add_sink_harness(self.0.as_ptr(), sink_harness.0.as_ptr());
268 }
269 }
270
271 /// Similar to gst_harness_add_sink, this allows you to specify a launch-line
272 /// instead of just an element name. See gst_harness_add_src_parse for details.
273 ///
274 /// MT safe.
275 /// ## `launchline`
276 /// a `gchar` with the name of a [`gst::Element`][crate::gst::Element]
277 #[doc(alias = "gst_harness_add_sink_parse")]
278 pub fn add_sink_parse(&mut self, launchline: &str) {
279 unsafe {
280 ffi::gst_harness_add_sink_parse(self.0.as_ptr(), launchline.to_glib_none().0);
281 }
282 }
283
284 /// Similar to gst_harness_add_src_harness, this is a convenience to
285 /// directly create a src-harness using the `src_element_name` name specified.
286 ///
287 /// MT safe.
288 /// ## `src_element_name`
289 /// a `gchar` with the name of a [`gst::Element`][crate::gst::Element]
290 /// ## `has_clock_wait`
291 /// a `gboolean` specifying if the [`gst::Element`][crate::gst::Element] uses
292 /// gst_clock_wait_id internally.
293 #[doc(alias = "gst_harness_add_src")]
294 pub fn add_src(&mut self, src_element_name: &str, has_clock_wait: bool) {
295 unsafe {
296 ffi::gst_harness_add_src(
297 self.0.as_ptr(),
298 src_element_name.to_glib_none().0,
299 has_clock_wait.into_glib(),
300 );
301 }
302 }
303
304 /// A src-harness is a great way of providing the [`Harness`][crate::Harness] with data.
305 /// By adding a src-type [`gst::Element`][crate::gst::Element], it is then easy to use functions like
306 /// gst_harness_push_from_src or gst_harness_src_crank_and_push_many
307 /// to provide your harnessed element with input. The `has_clock_wait` variable
308 /// is a great way to control you src-element with, in that you can have it
309 /// produce a buffer for you by simply cranking the clock, and not have it
310 /// spin out of control producing buffers as fast as possible.
311 ///
312 /// If a src-harness already exists it will be replaced.
313 ///
314 /// MT safe.
315 /// ## `src_harness`
316 /// a [`Harness`][crate::Harness] to be added as a src-harness.
317 /// ## `has_clock_wait`
318 /// a `gboolean` specifying if the [`gst::Element`][crate::gst::Element] uses
319 /// gst_clock_wait_id internally.
320 #[doc(alias = "gst_harness_add_src_harness")]
321 pub fn add_src_harness(&mut self, src_harness: Harness, has_clock_wait: bool) {
322 unsafe {
323 let src_harness = mem::ManuallyDrop::new(src_harness);
324 ffi::gst_harness_add_src_harness(
325 self.0.as_ptr(),
326 src_harness.0.as_ptr(),
327 has_clock_wait.into_glib(),
328 );
329 }
330 }
331
332 /// Similar to gst_harness_add_src, this allows you to specify a launch-line,
333 /// which can be useful for both having more then one [`gst::Element`][crate::gst::Element] acting as your
334 /// src (Like a src producing raw buffers, and then an encoder, providing encoded
335 /// data), but also by allowing you to set properties like "is-live" directly on
336 /// the elements.
337 ///
338 /// MT safe.
339 /// ## `launchline`
340 /// a `gchar` describing a gst-launch type line
341 /// ## `has_clock_wait`
342 /// a `gboolean` specifying if the [`gst::Element`][crate::gst::Element] uses
343 /// gst_clock_wait_id internally.
344 #[doc(alias = "gst_harness_add_src_parse")]
345 pub fn add_src_parse(&mut self, launchline: &str, has_clock_wait: bool) {
346 unsafe {
347 ffi::gst_harness_add_src_parse(
348 self.0.as_ptr(),
349 launchline.to_glib_none().0,
350 has_clock_wait.into_glib(),
351 );
352 }
353 }
354
355 /// The number of `GstBuffers` currently in the [`Harness`][crate::Harness] sinkpad `GAsyncQueue`
356 ///
357 /// MT safe.
358 ///
359 /// # Returns
360 ///
361 /// a `guint` number of buffers in the queue
362 #[doc(alias = "gst_harness_buffers_in_queue")]
363 pub fn buffers_in_queue(&self) -> u32 {
364 unsafe { ffi::gst_harness_buffers_in_queue(self.0.as_ptr()) }
365 }
366
367 /// The total number of `GstBuffers` that has arrived on the [`Harness`][crate::Harness] sinkpad.
368 /// This number includes buffers that have been dropped as well as buffers
369 /// that have already been pulled out.
370 ///
371 /// MT safe.
372 ///
373 /// # Returns
374 ///
375 /// a `guint` number of buffers received
376 #[doc(alias = "gst_harness_buffers_received")]
377 pub fn buffers_received(&self) -> u32 {
378 unsafe { ffi::gst_harness_buffers_received(self.0.as_ptr()) }
379 }
380
381 /// Similar to [`crank_single_clock_wait()`][Self::crank_single_clock_wait()], this is the function to use
382 /// if your harnessed element(s) are using more then one gst_clock_id_wait.
383 /// Failing to do so can (and will) make it racy which `GstClockID` you actually
384 /// are releasing, where as this function will process all the waits at the
385 /// same time, ensuring that one thread can't register another wait before
386 /// both are released.
387 ///
388 /// MT safe.
389 /// ## `waits`
390 /// a `guint` describing the number of `GstClockIDs` to crank
391 ///
392 /// # Returns
393 ///
394 /// a `gboolean` [`true`] if the "crank" was successful, [`false`] if not.
395 #[doc(alias = "gst_harness_crank_multiple_clock_waits")]
396 pub fn crank_multiple_clock_waits(&mut self, waits: u32) -> Result<(), glib::BoolError> {
397 unsafe {
398 glib::result_from_gboolean!(
399 ffi::gst_harness_crank_multiple_clock_waits(self.0.as_ptr(), waits),
400 "Failed to crank multiple clock waits",
401 )
402 }
403 }
404
405 /// A "crank" consists of three steps:
406 /// 1: Wait for a `GstClockID` to be registered with the [`TestClock`][crate::TestClock].
407 /// 2: Advance the [`TestClock`][crate::TestClock] to the time the `GstClockID` is waiting for.
408 /// 3: Release the `GstClockID` wait.
409 /// Together, this provides an easy way to not have to think about the details
410 /// around clocks and time, but still being able to write deterministic tests
411 /// that are dependent on this. A "crank" can be though of as the notion of
412 /// manually driving the clock forward to its next logical step.
413 ///
414 /// MT safe.
415 ///
416 /// # Returns
417 ///
418 /// a `gboolean` [`true`] if the "crank" was successful, [`false`] if not.
419 #[doc(alias = "gst_harness_crank_single_clock_wait")]
420 pub fn crank_single_clock_wait(&mut self) -> Result<(), glib::BoolError> {
421 unsafe {
422 glib::result_from_gboolean!(
423 ffi::gst_harness_crank_single_clock_wait(self.0.as_ptr()),
424 "Failed to crank single clock wait",
425 )
426 }
427 }
428
429 /// Allocates a buffer using a [`gst::BufferPool`][crate::gst::BufferPool] if present, or else using the
430 /// configured [`gst::Allocator`][crate::gst::Allocator] and [`gst::AllocationParams`][crate::gst::AllocationParams]
431 ///
432 /// MT safe.
433 /// ## `size`
434 /// a `gsize` specifying the size of the buffer
435 ///
436 /// # Returns
437 ///
438 /// a [`gst::Buffer`][crate::gst::Buffer] of size `size`
439 #[doc(alias = "gst_harness_create_buffer")]
440 pub fn create_buffer(&mut self, size: usize) -> Result<gst::Buffer, glib::BoolError> {
441 unsafe {
442 Option::<_>::from_glib_full(ffi::gst_harness_create_buffer(self.0.as_ptr(), size))
443 .ok_or_else(|| glib::bool_error!("Failed to create new buffer"))
444 }
445 }
446
447 /// Allows you to dump the `GstBuffers` the [`Harness`][crate::Harness] sinkpad `GAsyncQueue`
448 /// to a file.
449 ///
450 /// MT safe.
451 /// ## `filename`
452 /// a `gchar` with a the name of a file
453 #[doc(alias = "gst_harness_dump_to_file")]
454 pub fn dump_to_file(&mut self, filename: impl AsRef<path::Path>) {
455 let filename = filename.as_ref();
456 unsafe {
457 ffi::gst_harness_dump_to_file(self.0.as_ptr(), filename.to_glib_none().0);
458 }
459 }
460
461 /// The number of `GstEvents` currently in the [`Harness`][crate::Harness] sinkpad `GAsyncQueue`
462 ///
463 /// MT safe.
464 ///
465 /// # Returns
466 ///
467 /// a `guint` number of events in the queue
468 #[doc(alias = "gst_harness_events_in_queue")]
469 pub fn events_in_queue(&self) -> u32 {
470 unsafe { ffi::gst_harness_events_in_queue(self.0.as_ptr()) }
471 }
472
473 /// The total number of `GstEvents` that has arrived on the [`Harness`][crate::Harness] sinkpad
474 /// This number includes events handled by the harness as well as events
475 /// that have already been pulled out.
476 ///
477 /// MT safe.
478 ///
479 /// # Returns
480 ///
481 /// a `guint` number of events received
482 #[doc(alias = "gst_harness_events_received")]
483 pub fn events_received(&self) -> u32 {
484 unsafe { ffi::gst_harness_events_received(self.0.as_ptr()) }
485 }
486
487 /// Most useful in conjunction with gst_harness_new_parse, this will scan the
488 /// `GstElements` inside the [`Harness`][crate::Harness], and check if any of them matches
489 /// `element_name`. Typical usecase being that you need to access one of the
490 /// harnessed elements for properties and/or signals.
491 ///
492 /// MT safe.
493 /// ## `element_name`
494 /// a `gchar` with a [`gst::ElementFactory`][crate::gst::ElementFactory] name
495 ///
496 /// # Returns
497 ///
498 /// a [`gst::Element`][crate::gst::Element] or [`None`] if not found
499 #[doc(alias = "gst_harness_find_element")]
500 pub fn find_element(&mut self, element_name: &str) -> Option<gst::Element> {
501 unsafe {
502 // Work around https://gitlab.freedesktop.org/gstreamer/gstreamer/merge_requests/31
503 let ptr = ffi::gst_harness_find_element(self.0.as_ptr(), element_name.to_glib_none().0);
504
505 if ptr.is_null() {
506 return None;
507 }
508
509 // Clear floating flag if it is set
510 if glib::gobject_ffi::g_object_is_floating(ptr as *mut _) != glib::ffi::GFALSE {
511 glib::gobject_ffi::g_object_ref_sink(ptr as *mut _);
512 }
513
514 from_glib_full(ptr)
515 }
516 }
517
518 //pub fn get(&mut self, element_name: &str, first_property_name: &str, : /*Unknown conversion*//*Unimplemented*/Fundamental: VarArgs) {
519 // unsafe { TODO: call ffi::gst_harness_get() }
520 //}
521
522 //pub fn get_allocator(&mut self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams) {
523 // unsafe { TODO: call ffi::gst_harness_get_allocator() }
524 //}
525
526 /// Get the timestamp of the last [`gst::Buffer`][crate::gst::Buffer] pushed on the [`Harness`][crate::Harness] srcpad,
527 /// typically with gst_harness_push or gst_harness_push_from_src.
528 ///
529 /// MT safe.
530 ///
531 /// # Returns
532 ///
533 /// a `GstClockTime` with the timestamp or `GST_CLOCK_TIME_NONE` if no
534 /// [`gst::Buffer`][crate::gst::Buffer] has been pushed on the [`Harness`][crate::Harness] srcpad
535 #[doc(alias = "get_last_pushed_timestamp")]
536 #[doc(alias = "gst_harness_get_last_pushed_timestamp")]
537 pub fn last_pushed_timestamp(&self) -> Option<gst::ClockTime> {
538 unsafe { from_glib(ffi::gst_harness_get_last_pushed_timestamp(self.0.as_ptr())) }
539 }
540
541 /// Get the [`TestClock`][crate::TestClock]. Useful if specific operations on the testclock is
542 /// needed.
543 ///
544 /// MT safe.
545 ///
546 /// # Returns
547 ///
548 /// a [`TestClock`][crate::TestClock], or [`None`] if the testclock is not
549 /// present.
550 #[doc(alias = "get_testclock")]
551 #[doc(alias = "gst_harness_get_testclock")]
552 pub fn testclock(&self) -> Option<TestClock> {
553 unsafe { from_glib_full(ffi::gst_harness_get_testclock(self.0.as_ptr())) }
554 }
555
556 /// This will set the harnessed [`gst::Element`][crate::gst::Element] to [`gst::State::Playing`][crate::gst::State::Playing].
557 /// `GstElements` without a sink-[`gst::Pad`][crate::gst::Pad] and with the [`gst::ElementFlags::SOURCE`][crate::gst::ElementFlags::SOURCE]
558 /// flag set is considered a src [`gst::Element`][crate::gst::Element]
559 /// Non-src `GstElements` (like sinks and filters) are automatically set to
560 /// playing by the [`Harness`][crate::Harness], but src `GstElements` are not to avoid them
561 /// starting to produce buffers.
562 /// Hence, for src [`gst::Element`][crate::gst::Element] you must call [`play()`][Self::play()] explicitly.
563 ///
564 /// MT safe.
565 #[doc(alias = "gst_harness_play")]
566 pub fn play(&mut self) {
567 unsafe {
568 ffi::gst_harness_play(self.0.as_ptr());
569 }
570 }
571
572 /// Pulls a [`gst::Buffer`][crate::gst::Buffer] from the `GAsyncQueue` on the [`Harness`][crate::Harness] sinkpad. The pull
573 /// will timeout in 60 seconds. This is the standard way of getting a buffer
574 /// from a harnessed [`gst::Element`][crate::gst::Element].
575 ///
576 /// MT safe.
577 ///
578 /// # Returns
579 ///
580 /// a [`gst::Buffer`][crate::gst::Buffer] or [`None`] if timed out.
581 #[doc(alias = "gst_harness_pull")]
582 pub fn pull(&mut self) -> Result<gst::Buffer, glib::BoolError> {
583 unsafe {
584 Option::<_>::from_glib_full(ffi::gst_harness_pull(self.0.as_ptr()))
585 .ok_or_else(|| glib::bool_error!("Failed to pull buffer"))
586 }
587 }
588
589 /// Pulls a [`gst::Buffer`][crate::gst::Buffer] from the `GAsyncQueue` on the [`Harness`][crate::Harness] sinkpad. The pull
590 /// will block until an EOS event is received, or timeout in 60 seconds.
591 /// MT safe.
592 ///
593 /// # Returns
594 ///
595 /// [`true`] on success, [`false`] on timeout.
596 ///
597 /// ## `buf`
598 /// A [`gst::Buffer`][crate::gst::Buffer], or [`None`] if EOS or timeout occures
599 /// first.
600 #[cfg(feature = "v1_18")]
601 #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
602 #[doc(alias = "gst_harness_pull_until_eos")]
603 pub fn pull_until_eos(&mut self) -> Result<Option<gst::Buffer>, glib::BoolError> {
604 unsafe {
605 let mut buffer = ptr::null_mut();
606 let res = ffi::gst_harness_pull_until_eos(self.0.as_ptr(), &mut buffer);
607 if from_glib(res) {
608 Ok(from_glib_full(buffer))
609 } else {
610 Err(glib::bool_error!("Failed to pull buffer or EOS"))
611 }
612 }
613 }
614
615 /// Pulls an [`gst::Event`][crate::gst::Event] from the `GAsyncQueue` on the [`Harness`][crate::Harness] sinkpad.
616 /// Timeouts after 60 seconds similar to gst_harness_pull.
617 ///
618 /// MT safe.
619 ///
620 /// # Returns
621 ///
622 /// a [`gst::Event`][crate::gst::Event] or [`None`] if timed out.
623 #[doc(alias = "gst_harness_pull_event")]
624 pub fn pull_event(&mut self) -> Result<gst::Event, glib::BoolError> {
625 unsafe {
626 Option::<_>::from_glib_full(ffi::gst_harness_pull_event(self.0.as_ptr()))
627 .ok_or_else(|| glib::bool_error!("Failed to pull event"))
628 }
629 }
630
631 /// Pulls an [`gst::Event`][crate::gst::Event] from the `GAsyncQueue` on the [`Harness`][crate::Harness] srcpad.
632 /// Timeouts after 60 seconds similar to gst_harness_pull.
633 ///
634 /// MT safe.
635 ///
636 /// # Returns
637 ///
638 /// a [`gst::Event`][crate::gst::Event] or [`None`] if timed out.
639 #[doc(alias = "gst_harness_pull_upstream_event")]
640 pub fn pull_upstream_event(&mut self) -> Result<gst::Event, glib::BoolError> {
641 unsafe {
642 Option::<_>::from_glib_full(ffi::gst_harness_pull_upstream_event(self.0.as_ptr()))
643 .ok_or_else(|| glib::bool_error!("Failed to pull event"))
644 }
645 }
646
647 /// Pushes a [`gst::Buffer`][crate::gst::Buffer] on the [`Harness`][crate::Harness] srcpad. The standard way of
648 /// interacting with an harnessed element.
649 ///
650 /// MT safe.
651 /// ## `buffer`
652 /// a [`gst::Buffer`][crate::gst::Buffer] to push
653 ///
654 /// # Returns
655 ///
656 /// a [`gst::FlowReturn`][crate::gst::FlowReturn] with the result from the push
657 #[doc(alias = "gst_harness_push")]
658 pub fn push(&mut self, buffer: gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
659 unsafe {
660 try_from_glib(ffi::gst_harness_push(
661 self.0.as_ptr(),
662 buffer.into_glib_ptr(),
663 ))
664 }
665 }
666
667 /// Basically a gst_harness_push and a gst_harness_pull in one line. Reflects
668 /// the fact that you often want to do exactly this in your test: Push one buffer
669 /// in, and inspect the outcome.
670 ///
671 /// MT safe.
672 /// ## `buffer`
673 /// a [`gst::Buffer`][crate::gst::Buffer] to push
674 ///
675 /// # Returns
676 ///
677 /// a [`gst::Buffer`][crate::gst::Buffer] or [`None`] if timed out.
678 #[doc(alias = "gst_harness_push_and_pull")]
679 pub fn push_and_pull(&mut self, buffer: gst::Buffer) -> Result<gst::Buffer, glib::BoolError> {
680 unsafe {
681 Option::<_>::from_glib_full(ffi::gst_harness_push_and_pull(
682 self.0.as_ptr(),
683 buffer.into_glib_ptr(),
684 ))
685 .ok_or_else(|| glib::bool_error!("Failed to push and pull buffer"))
686 }
687 }
688
689 /// Pushes an [`gst::Event`][crate::gst::Event] on the [`Harness`][crate::Harness] srcpad.
690 ///
691 /// MT safe.
692 /// ## `event`
693 /// a [`gst::Event`][crate::gst::Event] to push
694 ///
695 /// # Returns
696 ///
697 /// a `gboolean` with the result from the push
698 #[doc(alias = "gst_harness_push_event")]
699 pub fn push_event(&mut self, event: gst::Event) -> bool {
700 unsafe {
701 from_glib(ffi::gst_harness_push_event(
702 self.0.as_ptr(),
703 event.into_glib_ptr(),
704 ))
705 }
706 }
707
708 /// Transfer data from the src-[`Harness`][crate::Harness] to the main-[`Harness`][crate::Harness]. It consists
709 /// of 4 steps:
710 /// 1: Make sure the src is started. (see: gst_harness_play)
711 /// 2: Crank the clock (see: gst_harness_crank_single_clock_wait)
712 /// 3: Pull a [`gst::Buffer`][crate::gst::Buffer] from the src-[`Harness`][crate::Harness] (see: gst_harness_pull)
713 /// 4: Push the same [`gst::Buffer`][crate::gst::Buffer] into the main-[`Harness`][crate::Harness] (see: gst_harness_push)
714 ///
715 /// MT safe.
716 ///
717 /// # Returns
718 ///
719 /// a [`gst::FlowReturn`][crate::gst::FlowReturn] with the result of the push
720 #[doc(alias = "gst_harness_push_from_src")]
721 pub fn push_from_src(&mut self) -> Result<gst::FlowSuccess, gst::FlowError> {
722 unsafe { try_from_glib(ffi::gst_harness_push_from_src(self.0.as_ptr())) }
723 }
724
725 /// Transfer one [`gst::Buffer`][crate::gst::Buffer] from the main-[`Harness`][crate::Harness] to the sink-[`Harness`][crate::Harness].
726 /// See gst_harness_push_from_src for details.
727 ///
728 /// MT safe.
729 ///
730 /// # Returns
731 ///
732 /// a [`gst::FlowReturn`][crate::gst::FlowReturn] with the result of the push
733 #[doc(alias = "gst_harness_push_to_sink")]
734 pub fn push_to_sink(&mut self) -> Result<gst::FlowSuccess, gst::FlowError> {
735 unsafe { try_from_glib(ffi::gst_harness_push_to_sink(self.0.as_ptr())) }
736 }
737
738 /// Pushes an [`gst::Event`][crate::gst::Event] on the [`Harness`][crate::Harness] sinkpad.
739 ///
740 /// MT safe.
741 /// ## `event`
742 /// a [`gst::Event`][crate::gst::Event] to push
743 ///
744 /// # Returns
745 ///
746 /// a `gboolean` with the result from the push
747 #[doc(alias = "gst_harness_push_upstream_event")]
748 pub fn push_upstream_event(&mut self, event: gst::Event) -> bool {
749 unsafe {
750 from_glib(ffi::gst_harness_push_upstream_event(
751 self.0.as_ptr(),
752 event.into_glib_ptr(),
753 ))
754 }
755 }
756
757 /// Get the min latency reported by any harnessed [`gst::Element`][crate::gst::Element].
758 ///
759 /// MT safe.
760 ///
761 /// # Returns
762 ///
763 /// a `GstClockTime` with min latency
764 #[doc(alias = "gst_harness_query_latency")]
765 pub fn query_latency(&self) -> Option<gst::ClockTime> {
766 unsafe { from_glib(ffi::gst_harness_query_latency(self.0.as_ptr())) }
767 }
768
769 //pub fn set(&mut self, element_name: &str, first_property_name: &str, : /*Unknown conversion*//*Unimplemented*/Fundamental: VarArgs) {
770 // unsafe { TODO: call ffi::gst_harness_set() }
771 //}
772
773 /// Setting this will make the harness block in the chain-function, and
774 /// then release when [`pull()`][Self::pull()] or [`try_pull()`][Self::try_pull()] is called.
775 /// Can be useful when wanting to control a src-element that is not implementing
776 /// `gst_clock_id_wait()` so it can't be controlled by the [`TestClock`][crate::TestClock], since
777 /// it otherwise would produce buffers as fast as possible.
778 ///
779 /// MT safe.
780 #[doc(alias = "gst_harness_set_blocking_push_mode")]
781 pub fn set_blocking_push_mode(&mut self) {
782 unsafe {
783 ffi::gst_harness_set_blocking_push_mode(self.0.as_ptr());
784 }
785 }
786
787 /// Sets the [`Harness`][crate::Harness] srcpad and sinkpad caps.
788 ///
789 /// MT safe.
790 /// ## `in_`
791 /// a [`gst::Caps`][crate::gst::Caps] to set on the harness srcpad
792 /// ## `out`
793 /// a [`gst::Caps`][crate::gst::Caps] to set on the harness sinkpad
794 #[doc(alias = "gst_harness_set_caps")]
795 pub fn set_caps(&mut self, in_: gst::Caps, out: gst::Caps) {
796 unsafe {
797 ffi::gst_harness_set_caps(self.0.as_ptr(), in_.into_glib_ptr(), out.into_glib_ptr());
798 }
799 }
800
801 /// Sets the [`Harness`][crate::Harness] srcpad and sinkpad caps using strings.
802 ///
803 /// MT safe.
804 /// ## `in_`
805 /// a `gchar` describing a [`gst::Caps`][crate::gst::Caps] to set on the harness srcpad
806 /// ## `out`
807 /// a `gchar` describing a [`gst::Caps`][crate::gst::Caps] to set on the harness sinkpad
808 #[doc(alias = "gst_harness_set_caps_str")]
809 pub fn set_caps_str(&mut self, in_: &str, out: &str) {
810 unsafe {
811 ffi::gst_harness_set_caps_str(
812 self.0.as_ptr(),
813 in_.to_glib_none().0,
814 out.to_glib_none().0,
815 );
816 }
817 }
818
819 /// When set to [`true`], instead of placing the buffers arriving from the harnessed
820 /// [`gst::Element`][crate::gst::Element] inside the sinkpads `GAsyncQueue`, they are instead unreffed.
821 ///
822 /// MT safe.
823 /// ## `drop_buffers`
824 /// a `gboolean` specifying to drop outgoing buffers or not
825 #[doc(alias = "gst_harness_set_drop_buffers")]
826 pub fn set_drop_buffers(&mut self, drop_buffers: bool) {
827 unsafe {
828 ffi::gst_harness_set_drop_buffers(self.0.as_ptr(), drop_buffers.into_glib());
829 }
830 }
831
832 /// As a convenience, a src-harness will forward [`gst::EventType::StreamStart`][crate::gst::EventType::StreamStart],
833 /// [`gst::EventType::Caps`][crate::gst::EventType::Caps] and [`gst::EventType::Segment`][crate::gst::EventType::Segment] to the main-harness if forwarding
834 /// is enabled, and forward any sticky-events from the main-harness to
835 /// the sink-harness. It will also forward the `GST_QUERY_ALLOCATION`.
836 ///
837 /// If forwarding is disabled, the user will have to either manually push
838 /// these events from the src-harness using [`src_push_event()`][Self::src_push_event()], or
839 /// create and push them manually. While this will allow full control and
840 /// inspection of these events, for the most cases having forwarding enabled
841 /// will be sufficient when writing a test where the src-harness' main function
842 /// is providing data for the main-harness.
843 ///
844 /// Forwarding is enabled by default.
845 ///
846 /// MT safe.
847 /// ## `forwarding`
848 /// a `gboolean` to enable/disable forwarding
849 #[doc(alias = "gst_harness_set_forwarding")]
850 pub fn set_forwarding(&mut self, forwarding: bool) {
851 unsafe {
852 ffi::gst_harness_set_forwarding(self.0.as_ptr(), forwarding.into_glib());
853 }
854 }
855
856 /// Sets the liveness reported by [`Harness`][crate::Harness] when receiving a latency-query.
857 /// The default is [`true`].
858 /// ## `is_live`
859 /// [`true`] for live, [`false`] for non-live
860 #[doc(alias = "gst_harness_set_live")]
861 #[cfg(feature = "v1_20")]
862 #[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
863 pub fn set_live(&mut self, is_live: bool) {
864 unsafe { ffi::gst_harness_set_live(self.0.as_ptr(), is_live.into_glib()) }
865 }
866
867 //pub fn set_propose_allocator<P: IsA<gst::Allocator>>(&mut self, allocator: Option<&P>, params: Option<&gst::AllocationParams>) {
868 // unsafe { TODO: call ffi::gst_harness_set_propose_allocator() }
869 //}
870
871 /// Sets the [`Harness`][crate::Harness] sinkpad caps.
872 ///
873 /// MT safe.
874 /// ## `caps`
875 /// a [`gst::Caps`][crate::gst::Caps] to set on the harness sinkpad
876 #[doc(alias = "gst_harness_set_sink_caps")]
877 pub fn set_sink_caps(&mut self, caps: gst::Caps) {
878 unsafe {
879 ffi::gst_harness_set_sink_caps(self.0.as_ptr(), caps.into_glib_ptr());
880 }
881 }
882
883 /// Sets the [`Harness`][crate::Harness] sinkpad caps using a string.
884 ///
885 /// MT safe.
886 /// ## `str`
887 /// a `gchar` describing a [`gst::Caps`][crate::gst::Caps] to set on the harness sinkpad
888 #[doc(alias = "gst_harness_set_sink_caps_str")]
889 pub fn set_sink_caps_str(&mut self, str: &str) {
890 unsafe {
891 ffi::gst_harness_set_sink_caps_str(self.0.as_ptr(), str.to_glib_none().0);
892 }
893 }
894
895 /// Sets the [`Harness`][crate::Harness] srcpad caps. This must be done before any buffers
896 /// can legally be pushed from the harness to the element.
897 ///
898 /// MT safe.
899 /// ## `caps`
900 /// a [`gst::Caps`][crate::gst::Caps] to set on the harness srcpad
901 #[doc(alias = "gst_harness_set_src_caps")]
902 pub fn set_src_caps(&mut self, caps: gst::Caps) {
903 unsafe {
904 ffi::gst_harness_set_src_caps(self.0.as_ptr(), caps.into_glib_ptr());
905 }
906 }
907
908 /// Sets the [`Harness`][crate::Harness] srcpad caps using a string. This must be done before
909 /// any buffers can legally be pushed from the harness to the element.
910 ///
911 /// MT safe.
912 /// ## `str`
913 /// a `gchar` describing a [`gst::Caps`][crate::gst::Caps] to set on the harness srcpad
914 #[doc(alias = "gst_harness_set_src_caps_str")]
915 pub fn set_src_caps_str(&mut self, str: &str) {
916 unsafe {
917 ffi::gst_harness_set_src_caps_str(self.0.as_ptr(), str.to_glib_none().0);
918 }
919 }
920
921 /// Advance the [`TestClock`][crate::TestClock] to a specific time.
922 ///
923 /// MT safe.
924 /// ## `time`
925 /// a `GstClockTime` to advance the clock to
926 ///
927 /// # Returns
928 ///
929 /// a `gboolean` [`true`] if the time could be set. [`false`] if not.
930 #[doc(alias = "gst_harness_set_time")]
931 pub fn set_time(&mut self, time: gst::ClockTime) -> Result<(), glib::BoolError> {
932 unsafe {
933 glib::result_from_gboolean!(
934 ffi::gst_harness_set_time(self.0.as_ptr(), time.into_glib()),
935 "Failed to set time",
936 )
937 }
938 }
939
940 /// Sets the min latency reported by [`Harness`][crate::Harness] when receiving a latency-query
941 /// ## `latency`
942 /// a `GstClockTime` specifying the latency
943 #[doc(alias = "gst_harness_set_upstream_latency")]
944 pub fn set_upstream_latency(&mut self, latency: gst::ClockTime) {
945 unsafe {
946 ffi::gst_harness_set_upstream_latency(self.0.as_ptr(), latency.into_glib());
947 }
948 }
949
950 /// Convenience that calls gst_harness_push_to_sink `pushes` number of times.
951 /// Will abort the pushing if any one push fails.
952 ///
953 /// MT safe.
954 /// ## `pushes`
955 /// a `gint` with the number of calls to gst_harness_push_to_sink
956 ///
957 /// # Returns
958 ///
959 /// a [`gst::FlowReturn`][crate::gst::FlowReturn] with the result of the push
960 #[doc(alias = "gst_harness_sink_push_many")]
961 pub fn sink_push_many(&mut self, pushes: u32) -> Result<gst::FlowSuccess, gst::FlowError> {
962 unsafe {
963 try_from_glib(ffi::gst_harness_sink_push_many(
964 self.0.as_ptr(),
965 pushes as i32,
966 ))
967 }
968 }
969
970 /// Transfer data from the src-[`Harness`][crate::Harness] to the main-[`Harness`][crate::Harness]. Similar to
971 /// gst_harness_push_from_src, this variant allows you to specify how many cranks
972 /// and how many pushes to perform. This can be useful for both moving a lot
973 /// of data at the same time, as well as cases when one crank does not equal one
974 /// buffer to push and v.v.
975 ///
976 /// MT safe.
977 /// ## `cranks`
978 /// a `gint` with the number of calls to gst_harness_crank_single_clock_wait
979 /// ## `pushes`
980 /// a `gint` with the number of calls to gst_harness_push
981 ///
982 /// # Returns
983 ///
984 /// a [`gst::FlowReturn`][crate::gst::FlowReturn] with the result of the push
985 #[doc(alias = "gst_harness_src_crank_and_push_many")]
986 pub fn src_crank_and_push_many(
987 &mut self,
988 cranks: u32,
989 pushes: u32,
990 ) -> Result<gst::FlowSuccess, gst::FlowError> {
991 unsafe {
992 try_from_glib(ffi::gst_harness_src_crank_and_push_many(
993 self.0.as_ptr(),
994 cranks as i32,
995 pushes as i32,
996 ))
997 }
998 }
999
1000 /// Similar to what gst_harness_src_push does with `GstBuffers`, this transfers
1001 /// a [`gst::Event`][crate::gst::Event] from the src-[`Harness`][crate::Harness] to the main-[`Harness`][crate::Harness]. Note that
1002 /// some `GstEvents` are being transferred automagically. Look at sink_forward_pad
1003 /// for details.
1004 ///
1005 /// MT safe.
1006 ///
1007 /// # Returns
1008 ///
1009 /// a `gboolean` with the result of the push
1010 #[doc(alias = "gst_harness_src_push_event")]
1011 pub fn src_push_event(&mut self) -> bool {
1012 unsafe { from_glib(ffi::gst_harness_src_push_event(self.0.as_ptr())) }
1013 }
1014
1015 //pub fn stress_custom_start<'a, P: Into<Option<&'a /*Ignored*/glib::Func>>, Q: Into<Option</*Unimplemented*/Fundamental: Pointer>>>(&mut self, init: P, callback: /*Unknown conversion*//*Unimplemented*/Func, data: Q, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1016 // unsafe { TODO: call ffi::gst_harness_stress_custom_start() }
1017 //}
1018
1019 //pub fn stress_property_start_full(&mut self, name: &str, value: /*Ignored*/&glib::Value, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1020 // unsafe { TODO: call ffi::gst_harness_stress_property_start_full() }
1021 //}
1022
1023 //pub fn stress_push_buffer_start_full(&mut self, caps: &mut gst::Caps, segment: /*Ignored*/&gst::Segment, buf: &mut gst::Buffer, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1024 // unsafe { TODO: call ffi::gst_harness_stress_push_buffer_start_full() }
1025 //}
1026
1027 //pub fn stress_push_buffer_with_cb_start_full<P: Into<Option</*Unimplemented*/Fundamental: Pointer>>>(&mut self, caps: &mut gst::Caps, segment: /*Ignored*/&gst::Segment, func: /*Unknown conversion*//*Unimplemented*/HarnessPrepareBufferFunc, data: P, notify: /*Unknown conversion*//*Unimplemented*/DestroyNotify, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1028 // unsafe { TODO: call ffi::gst_harness_stress_push_buffer_with_cb_start_full() }
1029 //}
1030
1031 //pub fn stress_push_event_start_full(&mut self, event: &mut gst::Event, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1032 // unsafe { TODO: call ffi::gst_harness_stress_push_event_start_full() }
1033 //}
1034
1035 //pub fn stress_push_event_with_cb_start_full<P: Into<Option</*Unimplemented*/Fundamental: Pointer>>>(&mut self, func: /*Unknown conversion*//*Unimplemented*/HarnessPrepareEventFunc, data: P, notify: /*Unknown conversion*//*Unimplemented*/DestroyNotify, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1036 // unsafe { TODO: call ffi::gst_harness_stress_push_event_with_cb_start_full() }
1037 //}
1038
1039 //pub fn stress_push_upstream_event_start_full(&mut self, event: &mut gst::Event, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1040 // unsafe { TODO: call ffi::gst_harness_stress_push_upstream_event_start_full() }
1041 //}
1042
1043 //pub fn stress_push_upstream_event_with_cb_start_full<P: Into<Option</*Unimplemented*/Fundamental: Pointer>>>(&mut self, func: /*Unknown conversion*//*Unimplemented*/HarnessPrepareEventFunc, data: P, notify: /*Unknown conversion*//*Unimplemented*/DestroyNotify, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1044 // unsafe { TODO: call ffi::gst_harness_stress_push_upstream_event_with_cb_start_full() }
1045 //}
1046
1047 //pub fn stress_requestpad_start_full(&mut self, templ: /*Ignored*/&gst::PadTemplate, name: &str, caps: &mut gst::Caps, release: bool, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1048 // unsafe { TODO: call ffi::gst_harness_stress_requestpad_start_full() }
1049 //}
1050
1051 //pub fn stress_statechange_start_full(&mut self, sleep: libc::c_ulong) -> /*Ignored*/Option<HarnessThread> {
1052 // unsafe { TODO: call ffi::gst_harness_stress_statechange_start_full() }
1053 //}
1054
1055 /// Pulls all pending data from the harness and returns it as a single buffer.
1056 ///
1057 /// # Returns
1058 ///
1059 /// the data as a buffer. Unref with `gst_buffer_unref()`
1060 /// when no longer needed.
1061 #[doc(alias = "gst_harness_take_all_data_as_buffer")]
1062 pub fn take_all_data_as_buffer(&mut self) -> Result<gst::Buffer, glib::BoolError> {
1063 unsafe {
1064 Option::<_>::from_glib_full(ffi::gst_harness_take_all_data_as_buffer(self.0.as_ptr()))
1065 .ok_or_else(|| glib::bool_error!("Failed to take all data as buffer"))
1066 }
1067 }
1068
1069 /// Pulls all pending data from the harness and returns it as a single [`glib::Bytes`][crate::glib::Bytes].
1070 ///
1071 /// # Returns
1072 ///
1073 /// a pointer to the data, newly allocated. Free
1074 /// with `g_free()` when no longer needed.
1075 #[doc(alias = "gst_harness_take_all_data_as_bytes")]
1076 pub fn take_all_data_as_bytes(&mut self) -> Result<glib::Bytes, glib::BoolError> {
1077 unsafe {
1078 Option::<_>::from_glib_full(ffi::gst_harness_take_all_data_as_bytes(self.0.as_ptr()))
1079 .ok_or_else(|| glib::bool_error!("Failed to take all data as bytes"))
1080 }
1081 }
1082
1083 /// Pulls a [`gst::Buffer`][crate::gst::Buffer] from the `GAsyncQueue` on the [`Harness`][crate::Harness] sinkpad. Unlike
1084 /// gst_harness_pull this will not wait for any buffers if not any are present,
1085 /// and return [`None`] straight away.
1086 ///
1087 /// MT safe.
1088 ///
1089 /// # Returns
1090 ///
1091 /// a [`gst::Buffer`][crate::gst::Buffer] or [`None`] if no buffers are present in the `GAsyncQueue`
1092 #[doc(alias = "gst_harness_try_pull")]
1093 pub fn try_pull(&mut self) -> Option<gst::Buffer> {
1094 unsafe { from_glib_full(ffi::gst_harness_try_pull(self.0.as_ptr())) }
1095 }
1096
1097 /// Pulls an [`gst::Event`][crate::gst::Event] from the `GAsyncQueue` on the [`Harness`][crate::Harness] sinkpad.
1098 /// See gst_harness_try_pull for details.
1099 ///
1100 /// MT safe.
1101 ///
1102 /// # Returns
1103 ///
1104 /// a [`gst::Event`][crate::gst::Event] or [`None`] if no buffers are present in the `GAsyncQueue`
1105 #[doc(alias = "gst_harness_try_pull_event")]
1106 pub fn try_pull_event(&mut self) -> Option<gst::Event> {
1107 unsafe { from_glib_full(ffi::gst_harness_try_pull_event(self.0.as_ptr())) }
1108 }
1109
1110 /// Pulls an [`gst::Event`][crate::gst::Event] from the `GAsyncQueue` on the [`Harness`][crate::Harness] srcpad.
1111 /// See gst_harness_try_pull for details.
1112 ///
1113 /// MT safe.
1114 ///
1115 /// # Returns
1116 ///
1117 /// a [`gst::Event`][crate::gst::Event] or [`None`] if no buffers are present in the `GAsyncQueue`
1118 #[doc(alias = "gst_harness_try_pull_upstream_event")]
1119 pub fn try_pull_upstream_event(&mut self) -> Option<gst::Event> {
1120 unsafe { from_glib_full(ffi::gst_harness_try_pull_upstream_event(self.0.as_ptr())) }
1121 }
1122
1123 /// The number of `GstEvents` currently in the [`Harness`][crate::Harness] srcpad `GAsyncQueue`
1124 ///
1125 /// MT safe.
1126 ///
1127 /// # Returns
1128 ///
1129 /// a `guint` number of events in the queue
1130 #[doc(alias = "gst_harness_upstream_events_in_queue")]
1131 pub fn upstream_events_in_queue(&self) -> u32 {
1132 unsafe { ffi::gst_harness_upstream_events_in_queue(self.0.as_ptr()) }
1133 }
1134
1135 /// The total number of `GstEvents` that has arrived on the [`Harness`][crate::Harness] srcpad
1136 /// This number includes events handled by the harness as well as events
1137 /// that have already been pulled out.
1138 ///
1139 /// MT safe.
1140 ///
1141 /// # Returns
1142 ///
1143 /// a `guint` number of events received
1144 #[doc(alias = "gst_harness_upstream_events_received")]
1145 pub fn upstream_events_received(&self) -> u32 {
1146 unsafe { ffi::gst_harness_upstream_events_received(self.0.as_ptr()) }
1147 }
1148
1149 /// Sets the system [`gst::Clock`][crate::gst::Clock] on the [`Harness`][crate::Harness] [`gst::Element`][crate::gst::Element]
1150 ///
1151 /// MT safe.
1152 #[doc(alias = "gst_harness_use_systemclock")]
1153 pub fn use_systemclock(&mut self) {
1154 unsafe {
1155 ffi::gst_harness_use_systemclock(self.0.as_ptr());
1156 }
1157 }
1158
1159 /// Sets the [`TestClock`][crate::TestClock] on the [`Harness`][crate::Harness] [`gst::Element`][crate::gst::Element]
1160 ///
1161 /// MT safe.
1162 #[doc(alias = "gst_harness_use_testclock")]
1163 pub fn use_testclock(&mut self) {
1164 unsafe {
1165 ffi::gst_harness_use_testclock(self.0.as_ptr());
1166 }
1167 }
1168
1169 /// Waits for `timeout` seconds until `waits` number of `GstClockID` waits is
1170 /// registered with the [`TestClock`][crate::TestClock]. Useful for writing deterministic tests,
1171 /// where you want to make sure that an expected number of waits have been
1172 /// reached.
1173 ///
1174 /// MT safe.
1175 /// ## `waits`
1176 /// a `guint` describing the numbers of `GstClockID` registered with
1177 /// the [`TestClock`][crate::TestClock]
1178 /// ## `timeout`
1179 /// a `guint` describing how many seconds to wait for `waits` to be true
1180 ///
1181 /// # Returns
1182 ///
1183 /// a `gboolean` [`true`] if the waits have been registered, [`false`] if not.
1184 /// (Could be that it timed out waiting or that more waits than waits was found)
1185 #[doc(alias = "gst_harness_wait_for_clock_id_waits")]
1186 pub fn wait_for_clock_id_waits(
1187 &mut self,
1188 waits: u32,
1189 timeout: u32,
1190 ) -> Result<(), glib::BoolError> {
1191 unsafe {
1192 glib::result_from_gboolean!(
1193 ffi::gst_harness_wait_for_clock_id_waits(self.0.as_ptr(), waits, timeout),
1194 "Failed to wait for clock id waits",
1195 )
1196 }
1197 }
1198
1199 #[inline]
1200 unsafe fn from_glib_full(ptr: *mut ffi::GstHarness) -> Harness {
1201 debug_assert!(!ptr.is_null());
1202
1203 Harness(ptr::NonNull::new_unchecked(ptr))
1204 }
1205
1206 /// Creates a new harness. Works like [`with_padnames()`][Self::with_padnames()], except it
1207 /// assumes the [`gst::Element`][crate::gst::Element] sinkpad is named "sink" and srcpad is named "src"
1208 ///
1209 /// MT safe.
1210 /// ## `element_name`
1211 /// a `gchar` describing the [`gst::Element`][crate::gst::Element] name
1212 ///
1213 /// # Returns
1214 ///
1215 /// a [`Harness`][crate::Harness], or [`None`] if the harness could
1216 /// not be created
1217 #[doc(alias = "gst_harness_new")]
1218 pub fn new(element_name: &str) -> Harness {
1219 assert_initialized_main_thread!();
1220 unsafe { Self::from_glib_full(ffi::gst_harness_new(element_name.to_glib_none().0)) }
1221 }
1222
1223 /// Creates a new empty harness. Use [`add_element_full()`][Self::add_element_full()] to add
1224 /// an [`gst::Element`][crate::gst::Element] to it.
1225 ///
1226 /// MT safe.
1227 ///
1228 /// # Returns
1229 ///
1230 /// a [`Harness`][crate::Harness], or [`None`] if the harness could
1231 /// not be created
1232 #[doc(alias = "gst_harness_new_empty")]
1233 pub fn new_empty() -> Harness {
1234 assert_initialized_main_thread!();
1235 unsafe { Self::from_glib_full(ffi::gst_harness_new_empty()) }
1236 }
1237
1238 /// Creates a new harness.
1239 ///
1240 /// MT safe.
1241 /// ## `element`
1242 /// a [`gst::Element`][crate::gst::Element] to attach the harness to (transfer none)
1243 /// ## `hsrc`
1244 /// a [`gst::StaticPadTemplate`][crate::gst::StaticPadTemplate] describing the harness srcpad.
1245 /// [`None`] will not create a harness srcpad.
1246 /// ## `element_sinkpad_name`
1247 /// a `gchar` with the name of the element
1248 /// sinkpad that is then linked to the harness srcpad. Can be a static or request
1249 /// or a sometimes pad that has been added. [`None`] will not get/request a sinkpad
1250 /// from the element. (Like if the element is a src.)
1251 /// ## `hsink`
1252 /// a [`gst::StaticPadTemplate`][crate::gst::StaticPadTemplate] describing the harness sinkpad.
1253 /// [`None`] will not create a harness sinkpad.
1254 /// ## `element_srcpad_name`
1255 /// a `gchar` with the name of the element
1256 /// srcpad that is then linked to the harness sinkpad, similar to the
1257 /// `element_sinkpad_name`.
1258 ///
1259 /// # Returns
1260 ///
1261 /// a [`Harness`][crate::Harness], or [`None`] if the harness could
1262 /// not be created
1263 #[doc(alias = "gst_harness_new_full")]
1264 pub fn new_full<P: IsA<gst::Element>>(
1265 element: &P,
1266 hsrc: Option<&gst::StaticPadTemplate>,
1267 element_sinkpad_name: Option<&str>,
1268 hsink: Option<&gst::StaticPadTemplate>,
1269 element_srcpad_name: Option<&str>,
1270 ) -> Harness {
1271 assert_initialized_main_thread!();
1272 let element_sinkpad_name = element_sinkpad_name.to_glib_none();
1273 let element_srcpad_name = element_srcpad_name.to_glib_none();
1274 unsafe {
1275 Self::from_glib_full(ffi::gst_harness_new_full(
1276 element.as_ref().to_glib_none().0,
1277 hsrc.to_glib_none().0 as *mut _,
1278 element_sinkpad_name.0,
1279 hsink.to_glib_none().0 as *mut _,
1280 element_srcpad_name.0,
1281 ))
1282 }
1283 }
1284
1285 /// Creates a new harness, parsing the `launchline` and putting that in a [`gst::Bin`][crate::gst::Bin],
1286 /// and then attches the harness to the bin.
1287 ///
1288 /// MT safe.
1289 /// ## `launchline`
1290 /// a `gchar` describing a gst-launch type line
1291 ///
1292 /// # Returns
1293 ///
1294 /// a [`Harness`][crate::Harness], or [`None`] if the harness could
1295 /// not be created
1296 #[doc(alias = "gst_harness_new_parse")]
1297 pub fn new_parse(launchline: &str) -> Harness {
1298 assert_initialized_main_thread!();
1299 unsafe { Self::from_glib_full(ffi::gst_harness_new_parse(launchline.to_glib_none().0)) }
1300 }
1301
1302 /// Creates a new harness. Works in the same way as [`new_full()`][Self::new_full()], only
1303 /// that generic padtemplates are used for the harness src and sinkpads, which
1304 /// will be sufficient in most usecases.
1305 ///
1306 /// MT safe.
1307 /// ## `element`
1308 /// a [`gst::Element`][crate::gst::Element] to attach the harness to (transfer none)
1309 /// ## `element_sinkpad_name`
1310 /// a `gchar` with the name of the element
1311 /// sinkpad that is then linked to the harness srcpad. [`None`] does not attach a
1312 /// sinkpad
1313 /// ## `element_srcpad_name`
1314 /// a `gchar` with the name of the element
1315 /// srcpad that is then linked to the harness sinkpad. [`None`] does not attach a
1316 /// srcpad
1317 ///
1318 /// # Returns
1319 ///
1320 /// a [`Harness`][crate::Harness], or [`None`] if the harness could
1321 /// not be created
1322 #[doc(alias = "gst_harness_new_with_element")]
1323 pub fn with_element<P: IsA<gst::Element>>(
1324 element: &P,
1325 element_sinkpad_name: Option<&str>,
1326 element_srcpad_name: Option<&str>,
1327 ) -> Harness {
1328 skip_assert_initialized!();
1329 let element_sinkpad_name = element_sinkpad_name.to_glib_none();
1330 let element_srcpad_name = element_srcpad_name.to_glib_none();
1331 unsafe {
1332 Self::from_glib_full(ffi::gst_harness_new_with_element(
1333 element.as_ref().to_glib_none().0,
1334 element_sinkpad_name.0,
1335 element_srcpad_name.0,
1336 ))
1337 }
1338 }
1339
1340 /// Creates a new harness. Works like [`with_element()`][Self::with_element()],
1341 /// except you specify the factoryname of the [`gst::Element`][crate::gst::Element]
1342 ///
1343 /// MT safe.
1344 /// ## `element_name`
1345 /// a `gchar` describing the [`gst::Element`][crate::gst::Element] name
1346 /// ## `element_sinkpad_name`
1347 /// a `gchar` with the name of the element
1348 /// sinkpad that is then linked to the harness srcpad. [`None`] does not attach a
1349 /// sinkpad
1350 /// ## `element_srcpad_name`
1351 /// a `gchar` with the name of the element
1352 /// srcpad that is then linked to the harness sinkpad. [`None`] does not attach a
1353 /// srcpad
1354 ///
1355 /// # Returns
1356 ///
1357 /// a [`Harness`][crate::Harness], or [`None`] if the harness could
1358 /// not be created
1359 #[doc(alias = "gst_harness_new_with_padnames")]
1360 pub fn with_padnames(
1361 element_name: &str,
1362 element_sinkpad_name: Option<&str>,
1363 element_srcpad_name: Option<&str>,
1364 ) -> Harness {
1365 assert_initialized_main_thread!();
1366 let element_sinkpad_name = element_sinkpad_name.to_glib_none();
1367 let element_srcpad_name = element_srcpad_name.to_glib_none();
1368 unsafe {
1369 Self::from_glib_full(ffi::gst_harness_new_with_padnames(
1370 element_name.to_glib_none().0,
1371 element_sinkpad_name.0,
1372 element_srcpad_name.0,
1373 ))
1374 }
1375 }
1376
1377 #[doc(alias = "gst_harness_new_with_templates")]
1378 pub fn with_templates(
1379 element_name: &str,
1380 hsrc: Option<&gst::StaticPadTemplate>,
1381 hsink: Option<&gst::StaticPadTemplate>,
1382 ) -> Harness {
1383 assert_initialized_main_thread!();
1384 unsafe {
1385 Self::from_glib_full(ffi::gst_harness_new_with_templates(
1386 element_name.to_glib_none().0,
1387 hsrc.to_glib_none().0 as *mut _,
1388 hsink.to_glib_none().0 as *mut _,
1389 ))
1390 }
1391 }
1392
1393 //pub fn stress_thread_stop(t: /*Ignored*/&mut HarnessThread) -> u32 {
1394 // unsafe { TODO: call ffi::gst_harness_stress_thread_stop() }
1395 //}
1396
1397 #[doc(alias = "get_element")]
1398 pub fn element(&self) -> Option<gst::Element> {
1399 unsafe {
1400 // Work around https://gitlab.freedesktop.org/gstreamer/gstreamer/merge_requests/31
1401 let ptr = (*self.0.as_ptr()).element;
1402
1403 if ptr.is_null() {
1404 return None;
1405 }
1406
1407 // Clear floating flag if it is set
1408 if glib::gobject_ffi::g_object_is_floating(ptr as *mut _) != glib::ffi::GFALSE {
1409 glib::gobject_ffi::g_object_ref_sink(ptr as *mut _);
1410 }
1411
1412 from_glib_none(ptr)
1413 }
1414 }
1415
1416 #[doc(alias = "get_sinkpad")]
1417 pub fn sinkpad(&self) -> Option<gst::Pad> {
1418 unsafe {
1419 // Work around https://gitlab.freedesktop.org/gstreamer/gstreamer/merge_requests/31
1420 let ptr = (*self.0.as_ptr()).sinkpad;
1421
1422 if ptr.is_null() {
1423 return None;
1424 }
1425
1426 // Clear floating flag if it is set
1427 if glib::gobject_ffi::g_object_is_floating(ptr as *mut _) != glib::ffi::GFALSE {
1428 glib::gobject_ffi::g_object_ref_sink(ptr as *mut _);
1429 }
1430
1431 from_glib_none(ptr)
1432 }
1433 }
1434
1435 #[doc(alias = "get_srcpad")]
1436 pub fn srcpad(&self) -> Option<gst::Pad> {
1437 unsafe {
1438 // Work around https://gitlab.freedesktop.org/gstreamer/gstreamer/merge_requests/31
1439 let ptr = (*self.0.as_ptr()).srcpad;
1440
1441 if ptr.is_null() {
1442 return None;
1443 }
1444
1445 // Clear floating flag if it is set
1446 if glib::gobject_ffi::g_object_is_floating(ptr as *mut _) != glib::ffi::GFALSE {
1447 glib::gobject_ffi::g_object_ref_sink(ptr as *mut _);
1448 }
1449
1450 from_glib_none(ptr)
1451 }
1452 }
1453
1454 #[doc(alias = "get_sink_harness")]
1455 pub fn sink_harness(&self) -> Option<Ref> {
1456 unsafe {
1457 if (*self.0.as_ptr()).sink_harness.is_null() {
1458 None
1459 } else {
1460 Some(Ref(
1461 &*((&(*self.0.as_ptr()).sink_harness) as *const *mut ffi::GstHarness
1462 as *const Harness),
1463 ))
1464 }
1465 }
1466 }
1467
1468 #[doc(alias = "get_src_harness")]
1469 pub fn src_harness(&self) -> Option<Ref> {
1470 unsafe {
1471 if (*self.0.as_ptr()).src_harness.is_null() {
1472 None
1473 } else {
1474 Some(Ref(
1475 &*((&(*self.0.as_ptr()).src_harness) as *const *mut ffi::GstHarness
1476 as *const Harness),
1477 ))
1478 }
1479 }
1480 }
1481
1482 #[doc(alias = "get_mut_sink_harness")]
1483 pub fn sink_harness_mut(&mut self) -> Option<RefMut> {
1484 unsafe {
1485 if (*self.0.as_ptr()).sink_harness.is_null() {
1486 None
1487 } else {
1488 Some(RefMut(
1489 &mut *((&mut (*self.0.as_ptr()).sink_harness) as *mut *mut ffi::GstHarness
1490 as *mut Harness),
1491 ))
1492 }
1493 }
1494 }
1495
1496 #[doc(alias = "get_mut_src_harness")]
1497 pub fn src_harness_mut(&mut self) -> Option<RefMut> {
1498 unsafe {
1499 if (*self.0.as_ptr()).src_harness.is_null() {
1500 None
1501 } else {
1502 Some(RefMut(
1503 &mut *((&mut (*self.0.as_ptr()).src_harness) as *mut *mut ffi::GstHarness
1504 as *mut Harness),
1505 ))
1506 }
1507 }
1508 }
1509}
1510
1511#[derive(Debug)]
1512pub struct Ref<'a>(&'a Harness);
1513
1514impl ops::Deref for Ref<'_> {
1515 type Target = Harness;
1516
1517 #[inline]
1518 fn deref(&self) -> &Harness {
1519 self.0
1520 }
1521}
1522
1523#[derive(Debug)]
1524pub struct RefMut<'a>(&'a mut Harness);
1525
1526impl ops::Deref for RefMut<'_> {
1527 type Target = Harness;
1528
1529 #[inline]
1530 fn deref(&self) -> &Harness {
1531 self.0
1532 }
1533}
1534
1535impl ops::DerefMut for RefMut<'_> {
1536 #[inline]
1537 fn deref_mut(&mut self) -> &mut Harness {
1538 self.0
1539 }
1540}
1541
1542#[cfg(test)]
1543mod tests {
1544 use super::*;
1545
1546 #[test]
1547 fn test_identity_push_pull() {
1548 gst::init().unwrap();
1549
1550 let mut h = Harness::new("identity");
1551 h.set_src_caps_str("application/test");
1552 let buf = gst::Buffer::new();
1553 let buf = h.push_and_pull(buf);
1554 assert!(buf.is_ok());
1555 }
1556}