1use std::{ffi::CStr, future::Future, mem, num::NonZeroU64, ops::ControlFlow, pin::Pin};
4
5use glib::translate::*;
6use itertools::Itertools;
7
8use crate::{
9 ffi,
10 format::{
11 CompatibleFormattedValue, FormattedValue, SpecificFormattedValueFullRange,
12 SpecificFormattedValueIntrinsic,
13 },
14 prelude::*,
15 ClockTime, Element, ElementFlags, Event, Format, GenericFormattedValue, Pad, PadTemplate,
16 Plugin, QueryRef, Rank, State,
17};
18
19impl Element {
20 #[doc(alias = "gst_element_link_many")]
21 pub fn link_many<E: AsRef<Element> + Clone>(
22 elements: impl IntoIterator<Item = E>,
23 ) -> Result<(), glib::BoolError> {
24 skip_assert_initialized!();
25 for (src, dest) in elements.into_iter().tuple_windows() {
26 unsafe {
27 glib::result_from_gboolean!(
28 ffi::gst_element_link(
29 src.as_ref().to_glib_none().0,
30 dest.as_ref().to_glib_none().0,
31 ),
32 "Failed to link elements '{}' and '{}'",
33 src.as_ref().name(),
34 dest.as_ref().name(),
35 )?;
36 }
37 }
38
39 Ok(())
40 }
41
42 #[doc(alias = "gst_element_unlink_many")]
43 pub fn unlink_many<E: AsRef<Element> + Clone>(elements: impl IntoIterator<Item = E>) {
44 skip_assert_initialized!();
45 for (src, dest) in elements.into_iter().tuple_windows() {
46 unsafe {
47 ffi::gst_element_unlink(
48 src.as_ref().to_glib_none().0,
49 dest.as_ref().to_glib_none().0,
50 );
51 }
52 }
53 }
54
55 #[doc(alias = "gst_element_register")]
71 pub fn register(
72 plugin: Option<&Plugin>,
73 name: &str,
74 rank: Rank,
75 type_: glib::types::Type,
76 ) -> Result<(), glib::error::BoolError> {
77 skip_assert_initialized!();
78 unsafe {
79 glib::result_from_gboolean!(
80 ffi::gst_element_register(
81 plugin.to_glib_none().0,
82 name.to_glib_none().0,
83 rank.into_glib() as u32,
84 type_.into_glib()
85 ),
86 "Failed to register element factory"
87 )
88 }
89 }
90}
91
92#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
93pub enum ElementMessageType {
94 Error,
95 Warning,
96 Info,
97}
98
99#[derive(Debug, PartialEq, Eq)]
100pub struct NotifyWatchId(NonZeroU64);
101
102impl IntoGlib for NotifyWatchId {
103 type GlibType = libc::c_ulong;
104
105 #[inline]
106 fn into_glib(self) -> libc::c_ulong {
107 self.0.get() as libc::c_ulong
108 }
109}
110
111impl FromGlib<libc::c_ulong> for NotifyWatchId {
112 #[inline]
113 unsafe fn from_glib(val: libc::c_ulong) -> NotifyWatchId {
114 skip_assert_initialized!();
115 debug_assert_ne!(val, 0);
116 NotifyWatchId(NonZeroU64::new_unchecked(val as _))
117 }
118}
119
120pub trait ElementExtManual: IsA<Element> + 'static {
121 #[doc(alias = "get_element_class")]
122 #[inline]
123 fn element_class(&self) -> &glib::Class<Element> {
124 unsafe { self.unsafe_cast_ref::<Element>().class() }
125 }
126
127 #[doc(alias = "get_current_state")]
128 fn current_state(&self) -> State {
129 self.state(Some(ClockTime::ZERO)).1
130 }
131
132 #[doc(alias = "get_pending_state")]
133 fn pending_state(&self) -> State {
134 self.state(Some(ClockTime::ZERO)).2
135 }
136
137 #[doc(alias = "gst_element_query")]
153 fn query(&self, query: &mut QueryRef) -> bool {
154 unsafe {
155 from_glib(ffi::gst_element_query(
156 self.as_ref().to_glib_none().0,
157 query.as_mut_ptr(),
158 ))
159 }
160 }
161
162 #[doc(alias = "gst_element_send_event")]
178 fn send_event(&self, event: impl Into<Event>) -> bool {
179 unsafe {
180 from_glib(ffi::gst_element_send_event(
181 self.as_ref().to_glib_none().0,
182 event.into().into_glib_ptr(),
183 ))
184 }
185 }
186
187 #[doc(alias = "get_metadata")]
195 #[doc(alias = "gst_element_class_get_metadata")]
196 fn metadata<'a>(&self, key: &str) -> Option<&'a str> {
197 self.element_class().metadata(key)
198 }
199
200 #[doc(alias = "get_pad_template")]
210 #[doc(alias = "gst_element_class_get_pad_template")]
211 fn pad_template(&self, name: &str) -> Option<PadTemplate> {
212 self.element_class().pad_template(name)
213 }
214
215 #[doc(alias = "get_pad_template_list")]
223 #[doc(alias = "gst_element_class_get_pad_template_list")]
224 fn pad_template_list(&self) -> glib::List<PadTemplate> {
225 self.element_class().pad_template_list()
226 }
227
228 #[allow(clippy::too_many_arguments)]
255 #[doc(alias = "gst_element_message_full")]
256 fn message_full<T: crate::MessageErrorDomain>(
257 &self,
258 type_: ElementMessageType,
259 code: T,
260 message: Option<&str>,
261 debug: Option<&str>,
262 file: &str,
263 function: &str,
264 line: u32,
265 ) {
266 unsafe {
267 let type_ = match type_ {
268 ElementMessageType::Error => ffi::GST_MESSAGE_ERROR,
269 ElementMessageType::Warning => ffi::GST_MESSAGE_WARNING,
270 ElementMessageType::Info => ffi::GST_MESSAGE_INFO,
271 };
272
273 ffi::gst_element_message_full(
274 self.as_ref().to_glib_none().0,
275 type_,
276 T::domain().into_glib(),
277 code.code(),
278 message.to_glib_full(),
279 debug.to_glib_full(),
280 file.to_glib_none().0,
281 function.to_glib_none().0,
282 line as i32,
283 );
284 }
285 }
286
287 fn set_element_flags(&self, flags: ElementFlags) {
288 unsafe {
289 let ptr: *mut ffi::GstObject = self.as_ptr() as *mut _;
290 let _guard = self.as_ref().object_lock();
291 (*ptr).flags |= flags.into_glib();
292 }
293 }
294
295 fn unset_element_flags(&self, flags: ElementFlags) {
296 unsafe {
297 let ptr: *mut ffi::GstObject = self.as_ptr() as *mut _;
298 let _guard = self.as_ref().object_lock();
299 (*ptr).flags &= !flags.into_glib();
300 }
301 }
302
303 #[doc(alias = "get_element_flags")]
304 fn element_flags(&self) -> ElementFlags {
305 unsafe {
306 let ptr: *mut ffi::GstObject = self.as_ptr() as *mut _;
307 let _guard = self.as_ref().object_lock();
308 from_glib((*ptr).flags)
309 }
310 }
311
312 #[allow(clippy::too_many_arguments)]
339 #[doc(alias = "gst_element_message_full_with_details")]
340 fn message_full_with_details<T: crate::MessageErrorDomain>(
341 &self,
342 type_: ElementMessageType,
343 code: T,
344 message: Option<&str>,
345 debug: Option<&str>,
346 file: &str,
347 function: &str,
348 line: u32,
349 structure: crate::Structure,
350 ) {
351 unsafe {
352 let type_ = match type_ {
353 ElementMessageType::Error => ffi::GST_MESSAGE_ERROR,
354 ElementMessageType::Warning => ffi::GST_MESSAGE_WARNING,
355 ElementMessageType::Info => ffi::GST_MESSAGE_INFO,
356 };
357
358 ffi::gst_element_message_full_with_details(
359 self.as_ref().to_glib_none().0,
360 type_,
361 T::domain().into_glib(),
362 code.code(),
363 message.to_glib_full(),
364 debug.to_glib_full(),
365 file.to_glib_none().0,
366 function.to_glib_none().0,
367 line as i32,
368 structure.into_glib_ptr(),
369 );
370 }
371 }
372
373 fn post_error_message(&self, msg: crate::ErrorMessage) {
374 let crate::ErrorMessage {
375 error_domain,
376 error_code,
377 ref message,
378 ref debug,
379 filename,
380 function,
381 line,
382 } = msg;
383
384 unsafe {
385 ffi::gst_element_message_full(
386 self.as_ref().to_glib_none().0,
387 ffi::GST_MESSAGE_ERROR,
388 error_domain.into_glib(),
389 error_code,
390 message.to_glib_full(),
391 debug.to_glib_full(),
392 filename.to_glib_none().0,
393 function.to_glib_none().0,
394 line as i32,
395 );
396 }
397 }
398
399 #[doc(alias = "gst_element_iterate_pads")]
412 fn iterate_pads(&self) -> crate::Iterator<Pad> {
413 unsafe {
414 from_glib_full(ffi::gst_element_iterate_pads(
415 self.as_ref().to_glib_none().0,
416 ))
417 }
418 }
419
420 #[doc(alias = "gst_element_iterate_sink_pads")]
431 fn iterate_sink_pads(&self) -> crate::Iterator<Pad> {
432 unsafe {
433 from_glib_full(ffi::gst_element_iterate_sink_pads(
434 self.as_ref().to_glib_none().0,
435 ))
436 }
437 }
438
439 #[doc(alias = "gst_element_iterate_src_pads")]
450 fn iterate_src_pads(&self) -> crate::Iterator<Pad> {
451 unsafe {
452 from_glib_full(ffi::gst_element_iterate_src_pads(
453 self.as_ref().to_glib_none().0,
454 ))
455 }
456 }
457
458 #[doc(alias = "get_pads")]
459 #[doc(alias = "gst_element_foreach_pad")]
460 fn pads(&self) -> Vec<Pad> {
461 unsafe {
462 let elt: &ffi::GstElement = &*(self.as_ptr() as *const _);
463 let _guard = self.as_ref().object_lock();
464 FromGlibPtrContainer::from_glib_none(elt.pads)
465 }
466 }
467
468 #[doc(alias = "get_sink_pads")]
469 #[doc(alias = "gst_element_foreach_sink_pad")]
470 fn sink_pads(&self) -> Vec<Pad> {
471 unsafe {
472 let elt: &ffi::GstElement = &*(self.as_ptr() as *const _);
473 let _guard = self.as_ref().object_lock();
474 FromGlibPtrContainer::from_glib_none(elt.sinkpads)
475 }
476 }
477
478 #[doc(alias = "get_src_pads")]
479 #[doc(alias = "gst_element_foreach_src_pad")]
480 fn src_pads(&self) -> Vec<Pad> {
481 unsafe {
482 let elt: &ffi::GstElement = &*(self.as_ptr() as *const _);
483 let _guard = self.as_ref().object_lock();
484 FromGlibPtrContainer::from_glib_none(elt.srcpads)
485 }
486 }
487
488 #[doc(alias = "gst_element_foreach_pad")]
502 fn foreach_pad<F: FnMut(&Element, &Pad) -> ControlFlow<()>>(&self, func: F) {
503 unsafe extern "C" fn trampoline<F: FnMut(&Element, &Pad) -> ControlFlow<()>>(
504 element: *mut ffi::GstElement,
505 pad: *mut ffi::GstPad,
506 user_data: glib::ffi::gpointer,
507 ) -> glib::ffi::gboolean {
508 let element = from_glib_borrow(element);
509 let pad = from_glib_borrow(pad);
510 let callback = user_data as *mut F;
511 (*callback)(&element, &pad).is_continue().into_glib()
512 }
513
514 unsafe {
515 let mut func = func;
516 let func_ptr: &mut F = &mut func;
517
518 let _ = ffi::gst_element_foreach_pad(
519 self.as_ptr() as *mut _,
520 Some(trampoline::<F>),
521 func_ptr as *mut _ as *mut _,
522 );
523 }
524 }
525
526 #[doc(alias = "gst_element_foreach_sink_pad")]
540 fn foreach_sink_pad<F: FnMut(&Element, &Pad) -> ControlFlow<()>>(&self, func: F) {
541 unsafe extern "C" fn trampoline<P: FnMut(&Element, &Pad) -> ControlFlow<()>>(
542 element: *mut ffi::GstElement,
543 pad: *mut ffi::GstPad,
544 user_data: glib::ffi::gpointer,
545 ) -> glib::ffi::gboolean {
546 let element = from_glib_borrow(element);
547 let pad = from_glib_borrow(pad);
548 let callback = user_data as *mut P;
549 (*callback)(&element, &pad).is_continue().into_glib()
550 }
551
552 unsafe {
553 let mut func = func;
554 let func_ptr: &mut F = &mut func;
555
556 let _ = ffi::gst_element_foreach_sink_pad(
557 self.as_ptr() as *mut _,
558 Some(trampoline::<F>),
559 func_ptr as *mut _ as *mut _,
560 );
561 }
562 }
563
564 #[doc(alias = "gst_element_foreach_src_pad")]
578 fn foreach_src_pad<F: FnMut(&Element, &Pad) -> ControlFlow<()>>(&self, func: F) {
579 unsafe extern "C" fn trampoline<P: FnMut(&Element, &Pad) -> ControlFlow<()>>(
580 element: *mut ffi::GstElement,
581 pad: *mut ffi::GstPad,
582 user_data: glib::ffi::gpointer,
583 ) -> glib::ffi::gboolean {
584 let element = from_glib_borrow(element);
585 let pad = from_glib_borrow(pad);
586 let callback = user_data as *mut P;
587 (*callback)(&element, &pad).is_continue().into_glib()
588 }
589
590 unsafe {
591 let mut func = func;
592 let func_ptr: &mut F = &mut func;
593
594 let _ = ffi::gst_element_foreach_src_pad(
595 self.as_ptr() as *mut _,
596 Some(trampoline::<F>),
597 func_ptr as *mut _ as *mut _,
598 );
599 }
600 }
601
602 fn num_pads(&self) -> u16 {
603 unsafe {
604 let elt: &ffi::GstElement = &*(self.as_ptr() as *const _);
605 let _guard = self.as_ref().object_lock();
606 elt.numpads
607 }
608 }
609
610 fn num_sink_pads(&self) -> u16 {
611 unsafe {
612 let elt: &ffi::GstElement = &*(self.as_ptr() as *const _);
613 let _guard = self.as_ref().object_lock();
614 elt.numsinkpads
615 }
616 }
617
618 fn num_src_pads(&self) -> u16 {
619 unsafe {
620 let elt: &ffi::GstElement = &*(self.as_ptr() as *const _);
621 let _guard = self.as_ref().object_lock();
622 elt.numsrcpads
623 }
624 }
625
626 #[doc(alias = "gst_element_add_property_deep_notify_watch")]
637 fn add_property_deep_notify_watch(
638 &self,
639 property_name: Option<&str>,
640 include_value: bool,
641 ) -> NotifyWatchId {
642 let property_name = property_name.to_glib_none();
643 unsafe {
644 from_glib(ffi::gst_element_add_property_deep_notify_watch(
645 self.as_ref().to_glib_none().0,
646 property_name.0,
647 include_value.into_glib(),
648 ))
649 }
650 }
651
652 #[doc(alias = "gst_element_add_property_notify_watch")]
663 fn add_property_notify_watch(
664 &self,
665 property_name: Option<&str>,
666 include_value: bool,
667 ) -> NotifyWatchId {
668 let property_name = property_name.to_glib_none();
669 unsafe {
670 from_glib(ffi::gst_element_add_property_notify_watch(
671 self.as_ref().to_glib_none().0,
672 property_name.0,
673 include_value.into_glib(),
674 ))
675 }
676 }
677
678 #[doc(alias = "gst_element_remove_property_notify_watch")]
681 fn remove_property_notify_watch(&self, watch_id: NotifyWatchId) {
682 unsafe {
683 ffi::gst_element_remove_property_notify_watch(
684 self.as_ref().to_glib_none().0,
685 watch_id.into_glib(),
686 );
687 }
688 }
689
690 #[doc(alias = "gst_element_query_convert")]
705 fn query_convert<U: SpecificFormattedValueFullRange>(
706 &self,
707 src_val: impl FormattedValue,
708 ) -> Option<U> {
709 unsafe {
710 let mut dest_val = mem::MaybeUninit::uninit();
711 let ret = from_glib(ffi::gst_element_query_convert(
712 self.as_ref().to_glib_none().0,
713 src_val.format().into_glib(),
714 src_val.into_raw_value(),
715 U::default_format().into_glib(),
716 dest_val.as_mut_ptr(),
717 ));
718 if ret {
719 Some(U::from_raw(U::default_format(), dest_val.assume_init()))
720 } else {
721 None
722 }
723 }
724 }
725
726 #[doc(alias = "gst_element_query_convert")]
727 fn query_convert_generic(
728 &self,
729 src_val: impl FormattedValue,
730 dest_format: Format,
731 ) -> Option<GenericFormattedValue> {
732 unsafe {
733 let mut dest_val = mem::MaybeUninit::uninit();
734 let ret = from_glib(ffi::gst_element_query_convert(
735 self.as_ref().to_glib_none().0,
736 src_val.format().into_glib(),
737 src_val.into_raw_value(),
738 dest_format.into_glib(),
739 dest_val.as_mut_ptr(),
740 ));
741 if ret {
742 Some(GenericFormattedValue::new(
743 dest_format,
744 dest_val.assume_init(),
745 ))
746 } else {
747 None
748 }
749 }
750 }
751
752 #[doc(alias = "gst_element_query_duration")]
770 fn query_duration<T: SpecificFormattedValueIntrinsic>(&self) -> Option<T> {
771 unsafe {
772 let mut duration = mem::MaybeUninit::uninit();
773 let ret = from_glib(ffi::gst_element_query_duration(
774 self.as_ref().to_glib_none().0,
775 T::default_format().into_glib(),
776 duration.as_mut_ptr(),
777 ));
778 if ret {
779 try_from_glib(duration.assume_init()).ok()
780 } else {
781 None
782 }
783 }
784 }
785
786 #[doc(alias = "gst_element_query_duration")]
787 fn query_duration_generic(&self, format: Format) -> Option<GenericFormattedValue> {
788 unsafe {
789 let mut duration = mem::MaybeUninit::uninit();
790 let ret = from_glib(ffi::gst_element_query_duration(
791 self.as_ref().to_glib_none().0,
792 format.into_glib(),
793 duration.as_mut_ptr(),
794 ));
795 if ret {
796 Some(GenericFormattedValue::new(format, duration.assume_init()))
797 } else {
798 None
799 }
800 }
801 }
802
803 #[doc(alias = "gst_element_query_position")]
823 fn query_position<T: SpecificFormattedValueIntrinsic>(&self) -> Option<T> {
824 unsafe {
825 let mut cur = mem::MaybeUninit::uninit();
826 let ret = from_glib(ffi::gst_element_query_position(
827 self.as_ref().to_glib_none().0,
828 T::default_format().into_glib(),
829 cur.as_mut_ptr(),
830 ));
831 if ret {
832 try_from_glib(cur.assume_init()).ok()
833 } else {
834 None
835 }
836 }
837 }
838
839 #[doc(alias = "gst_element_query_position")]
840 fn query_position_generic(&self, format: Format) -> Option<GenericFormattedValue> {
841 unsafe {
842 let mut cur = mem::MaybeUninit::uninit();
843 let ret = from_glib(ffi::gst_element_query_position(
844 self.as_ref().to_glib_none().0,
845 format.into_glib(),
846 cur.as_mut_ptr(),
847 ));
848 if ret {
849 Some(GenericFormattedValue::new(format, cur.assume_init()))
850 } else {
851 None
852 }
853 }
854 }
855
856 #[doc(alias = "gst_element_seek")]
881 fn seek<V: FormattedValue>(
882 &self,
883 rate: f64,
884 flags: crate::SeekFlags,
885 start_type: crate::SeekType,
886 start: V,
887 stop_type: crate::SeekType,
888 stop: impl CompatibleFormattedValue<V>,
889 ) -> Result<(), glib::error::BoolError> {
890 let stop = stop.try_into_checked(start).unwrap();
891
892 unsafe {
893 glib::result_from_gboolean!(
894 ffi::gst_element_seek(
895 self.as_ref().to_glib_none().0,
896 rate,
897 start.format().into_glib(),
898 flags.into_glib(),
899 start_type.into_glib(),
900 start.into_raw_value(),
901 stop_type.into_glib(),
902 stop.into_raw_value(),
903 ),
904 "Failed to seek",
905 )
906 }
907 }
908
909 #[doc(alias = "gst_element_seek_simple")]
939 fn seek_simple(
940 &self,
941 seek_flags: crate::SeekFlags,
942 seek_pos: impl FormattedValue,
943 ) -> Result<(), glib::error::BoolError> {
944 unsafe {
945 glib::result_from_gboolean!(
946 ffi::gst_element_seek_simple(
947 self.as_ref().to_glib_none().0,
948 seek_pos.format().into_glib(),
949 seek_flags.into_glib(),
950 seek_pos.into_raw_value(),
951 ),
952 "Failed to seek",
953 )
954 }
955 }
956
957 #[doc(alias = "gst_element_call_async")]
972 fn call_async<F>(&self, func: F)
973 where
974 F: FnOnce(&Self) + Send + 'static,
975 {
976 let user_data: Box<Option<F>> = Box::new(Some(func));
977
978 unsafe extern "C" fn trampoline<O: IsA<Element>, F: FnOnce(&O) + Send + 'static>(
979 element: *mut ffi::GstElement,
980 user_data: glib::ffi::gpointer,
981 ) {
982 let user_data: &mut Option<F> = &mut *(user_data as *mut _);
983 let callback = user_data.take().unwrap();
984
985 callback(Element::from_glib_borrow(element).unsafe_cast_ref());
986 }
987
988 unsafe extern "C" fn free_user_data<O: IsA<Element>, F: FnOnce(&O) + Send + 'static>(
989 user_data: glib::ffi::gpointer,
990 ) {
991 let _: Box<Option<F>> = Box::from_raw(user_data as *mut _);
992 }
993
994 unsafe {
995 ffi::gst_element_call_async(
996 self.as_ref().to_glib_none().0,
997 Some(trampoline::<Self, F>),
998 Box::into_raw(user_data) as *mut _,
999 Some(free_user_data::<Self, F>),
1000 );
1001 }
1002 }
1003
1004 fn call_async_future<F, T>(&self, func: F) -> Pin<Box<dyn Future<Output = T> + Send + 'static>>
1005 where
1006 F: FnOnce(&Self) -> T + Send + 'static,
1007 T: Send + 'static,
1008 {
1009 use futures_channel::oneshot;
1010
1011 let (sender, receiver) = oneshot::channel();
1012
1013 self.call_async(move |element| {
1014 let _ = sender.send(func(element));
1015 });
1016
1017 Box::pin(async move { receiver.await.expect("sender dropped") })
1018 }
1019
1020 #[doc(alias = "get_current_running_time")]
1029 #[doc(alias = "gst_element_get_current_running_time")]
1030 fn current_running_time(&self) -> Option<crate::ClockTime> {
1031 let base_time = self.base_time();
1032 let clock_time = self.current_clock_time();
1033
1034 clock_time
1035 .zip(base_time)
1036 .and_then(|(ct, bt)| ct.checked_sub(bt))
1037 }
1038
1039 #[doc(alias = "get_current_clock_time")]
1047 #[doc(alias = "gst_element_get_current_clock_time")]
1048 fn current_clock_time(&self) -> Option<crate::ClockTime> {
1049 if let Some(clock) = self.clock() {
1050 clock.time()
1051 } else {
1052 crate::ClockTime::NONE
1053 }
1054 }
1055
1056 #[doc(alias = "gst_element_get_request_pad")]
1072 #[doc(alias = "get_request_pad")]
1073 #[doc(alias = "gst_element_request_pad_simple")]
1074 fn request_pad_simple(&self, name: &str) -> Option<Pad> {
1075 unsafe {
1076 #[cfg(feature = "v1_20")]
1077 {
1078 from_glib_full(ffi::gst_element_request_pad_simple(
1079 self.as_ref().to_glib_none().0,
1080 name.to_glib_none().0,
1081 ))
1082 }
1083 #[cfg(not(feature = "v1_20"))]
1084 {
1085 from_glib_full(ffi::gst_element_get_request_pad(
1086 self.as_ref().to_glib_none().0,
1087 name.to_glib_none().0,
1088 ))
1089 }
1090 }
1091 }
1092
1093 #[doc(alias = "gst_element_link")]
1108 fn link(&self, dest: &impl IsA<Element>) -> Result<(), glib::error::BoolError> {
1109 unsafe {
1110 glib::result_from_gboolean!(
1111 ffi::gst_element_link(
1112 self.as_ref().to_glib_none().0,
1113 dest.as_ref().to_glib_none().0
1114 ),
1115 "Failed to link elements '{}' and '{}'",
1116 self.as_ref().name(),
1117 dest.as_ref().name(),
1118 )
1119 }
1120 }
1121
1122 #[doc(alias = "gst_element_link_filtered")]
1140 fn link_filtered(
1141 &self,
1142 dest: &impl IsA<Element>,
1143 filter: &crate::Caps,
1144 ) -> Result<(), glib::error::BoolError> {
1145 unsafe {
1146 glib::result_from_gboolean!(
1147 ffi::gst_element_link_filtered(
1148 self.as_ref().to_glib_none().0,
1149 dest.as_ref().to_glib_none().0,
1150 filter.to_glib_none().0
1151 ),
1152 "Failed to link elements '{}' and '{}' with filter '{:?}'",
1153 self.as_ref().name(),
1154 dest.as_ref().name(),
1155 filter,
1156 )
1157 }
1158 }
1159
1160 #[doc(alias = "gst_element_link_pads")]
1177 fn link_pads(
1178 &self,
1179 srcpadname: Option<&str>,
1180 dest: &impl IsA<Element>,
1181 destpadname: Option<&str>,
1182 ) -> Result<(), glib::error::BoolError> {
1183 unsafe {
1184 glib::result_from_gboolean!(
1185 ffi::gst_element_link_pads(
1186 self.as_ref().to_glib_none().0,
1187 srcpadname.to_glib_none().0,
1188 dest.as_ref().to_glib_none().0,
1189 destpadname.to_glib_none().0
1190 ),
1191 "Failed to link pads '{}' and '{}'",
1192 if let Some(srcpadname) = srcpadname {
1193 format!("{}:{}", self.as_ref().name(), srcpadname)
1194 } else {
1195 format!("{}:*", self.as_ref().name())
1196 },
1197 if let Some(destpadname) = destpadname {
1198 format!("{}:{}", dest.as_ref().name(), destpadname)
1199 } else {
1200 format!("{}:*", dest.as_ref().name())
1201 },
1202 )
1203 }
1204 }
1205
1206 #[doc(alias = "gst_element_link_pads_filtered")]
1226 fn link_pads_filtered(
1227 &self,
1228 srcpadname: Option<&str>,
1229 dest: &impl IsA<Element>,
1230 destpadname: Option<&str>,
1231 filter: &crate::Caps,
1232 ) -> Result<(), glib::error::BoolError> {
1233 unsafe {
1234 glib::result_from_gboolean!(
1235 ffi::gst_element_link_pads_filtered(
1236 self.as_ref().to_glib_none().0,
1237 srcpadname.to_glib_none().0,
1238 dest.as_ref().to_glib_none().0,
1239 destpadname.to_glib_none().0,
1240 filter.to_glib_none().0
1241 ),
1242 "Failed to link pads '{}' and '{}' with filter '{:?}'",
1243 if let Some(srcpadname) = srcpadname {
1244 format!("{}:{}", self.as_ref().name(), srcpadname)
1245 } else {
1246 format!("{}:*", self.as_ref().name())
1247 },
1248 if let Some(destpadname) = destpadname {
1249 format!("{}:{}", dest.as_ref().name(), destpadname)
1250 } else {
1251 format!("{}:*", dest.as_ref().name())
1252 },
1253 filter,
1254 )
1255 }
1256 }
1257
1258 #[doc(alias = "gst_element_link_pads_full")]
1283 fn link_pads_full(
1284 &self,
1285 srcpadname: Option<&str>,
1286 dest: &impl IsA<Element>,
1287 destpadname: Option<&str>,
1288 flags: crate::PadLinkCheck,
1289 ) -> Result<(), glib::error::BoolError> {
1290 unsafe {
1291 glib::result_from_gboolean!(
1292 ffi::gst_element_link_pads_full(
1293 self.as_ref().to_glib_none().0,
1294 srcpadname.to_glib_none().0,
1295 dest.as_ref().to_glib_none().0,
1296 destpadname.to_glib_none().0,
1297 flags.into_glib()
1298 ),
1299 "Failed to link pads '{}' and '{}' with flags '{:?}'",
1300 if let Some(srcpadname) = srcpadname {
1301 format!("{}:{}", self.as_ref().name(), srcpadname)
1302 } else {
1303 format!("{}:*", self.as_ref().name())
1304 },
1305 if let Some(destpadname) = destpadname {
1306 format!("{}:{}", dest.as_ref().name(), destpadname)
1307 } else {
1308 format!("{}:*", dest.as_ref().name())
1309 },
1310 flags,
1311 )
1312 }
1313 }
1314}
1315
1316impl<O: IsA<Element>> ElementExtManual for O {}
1317
1318pub unsafe trait ElementClassExt {
1319 #[doc(alias = "get_metadata")]
1327 #[doc(alias = "gst_element_class_get_metadata")]
1328 fn metadata<'a>(&self, key: &str) -> Option<&'a str> {
1329 unsafe {
1330 let klass = self as *const _ as *const ffi::GstElementClass;
1331
1332 let ptr = ffi::gst_element_class_get_metadata(klass as *mut _, key.to_glib_none().0);
1333
1334 if ptr.is_null() {
1335 None
1336 } else {
1337 Some(CStr::from_ptr(ptr).to_str().unwrap())
1338 }
1339 }
1340 }
1341
1342 #[doc(alias = "get_pad_template")]
1355 #[doc(alias = "gst_element_class_get_pad_template")]
1356 fn pad_template(&self, name: &str) -> Option<PadTemplate> {
1357 unsafe {
1358 let klass = self as *const _ as *const ffi::GstElementClass;
1359
1360 from_glib_none(ffi::gst_element_class_get_pad_template(
1361 klass as *mut _,
1362 name.to_glib_none().0,
1363 ))
1364 }
1365 }
1366
1367 #[doc(alias = "get_pad_template_list")]
1378 #[doc(alias = "gst_element_class_get_pad_template_list")]
1379 fn pad_template_list(&self) -> glib::List<PadTemplate> {
1380 unsafe {
1381 let klass = self as *const _ as *const ffi::GstElementClass;
1382
1383 glib::List::from_glib_none(ffi::gst_element_class_get_pad_template_list(
1384 klass as *mut _,
1385 ))
1386 }
1387 }
1388}
1389
1390unsafe impl<T: IsA<Element> + glib::object::IsClass> ElementClassExt for glib::object::Class<T> {}
1391
1392#[doc(alias = "GST_ELEMENT_METADATA_AUTHOR")]
1396pub static ELEMENT_METADATA_AUTHOR: &glib::GStr =
1397 unsafe { glib::GStr::from_utf8_with_nul_unchecked(ffi::GST_ELEMENT_METADATA_AUTHOR) };
1398#[doc(alias = "GST_ELEMENT_METADATA_DESCRIPTION")]
1401pub static ELEMENT_METADATA_DESCRIPTION: &glib::GStr =
1402 unsafe { glib::GStr::from_utf8_with_nul_unchecked(ffi::GST_ELEMENT_METADATA_DESCRIPTION) };
1403#[doc(alias = "GST_ELEMENT_METADATA_DOC_URI")]
1406pub static ELEMENT_METADATA_DOC_URI: &glib::GStr =
1407 unsafe { glib::GStr::from_utf8_with_nul_unchecked(ffi::GST_ELEMENT_METADATA_DOC_URI) };
1408#[doc(alias = "GST_ELEMENT_METADATA_ICON_NAME")]
1412pub static ELEMENT_METADATA_ICON_NAME: &glib::GStr =
1413 unsafe { glib::GStr::from_utf8_with_nul_unchecked(ffi::GST_ELEMENT_METADATA_ICON_NAME) };
1414#[doc(alias = "GST_ELEMENT_METADATA_KLASS")]
1418pub static ELEMENT_METADATA_KLASS: &glib::GStr =
1419 unsafe { glib::GStr::from_utf8_with_nul_unchecked(ffi::GST_ELEMENT_METADATA_KLASS) };
1420#[doc(alias = "GST_ELEMENT_METADATA_LONGNAME")]
1422pub static ELEMENT_METADATA_LONGNAME: &glib::GStr =
1423 unsafe { glib::GStr::from_utf8_with_nul_unchecked(ffi::GST_ELEMENT_METADATA_LONGNAME) };
1424
1425#[doc(alias = "GST_ELEMENT_ERROR")]
1426#[doc(alias = "GST_ELEMENT_ERROR_WITH_DETAILS")]
1427#[macro_export]
1428macro_rules! element_error(
1429 ($obj:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*]) => { {
1430 use $crate::prelude::ElementExtManual;
1431 $obj.message_full(
1432 $crate::ElementMessageType::Error,
1433 $err,
1434 Some(&format!($($msg)*)),
1435 Some(&format!($($debug)*)),
1436 file!(),
1437 $crate::glib::function_name!(),
1438 line!(),
1439 );
1440 }};
1441 ($obj:expr, $err:expr, ($($msg:tt)*)) => { {
1442 use $crate::prelude::ElementExtManual;
1443 $obj.message_full(
1444 $crate::ElementMessageType::Error,
1445 $err,
1446 Some(&format!($($msg)*)),
1447 None,
1448 file!(),
1449 $crate::glib::function_name!(),
1450 line!(),
1451 );
1452 }};
1453 ($obj:expr, $err:expr, [$($debug:tt)*]) => { {
1454 use $crate::prelude::ElementExtManual;
1455 $obj.message_full(
1456 $crate::ElementMessageType::Error,
1457 $err,
1458 None,
1459 Some(&format!($($debug)*)),
1460 file!(),
1461 $crate::glib::function_name!(),
1462 line!(),
1463 );
1464 }};
1465
1466 ($obj:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*], details: $details:expr) => { {
1467 use $crate::prelude::ElementExtManual;
1468 $obj.message_full_with_details(
1469 $crate::ElementMessageType::Error,
1470 $err,
1471 Some(&format!($($msg)*)),
1472 Some(&format!($($debug)*)),
1473 file!(),
1474 $crate::glib::function_name!(),
1475 line!(),
1476 $details,
1477 );
1478 }};
1479 ($obj:expr, $err:expr, ($($msg:tt)*), details: $details:expr) => { {
1480 use $crate::prelude::ElementExtManual;
1481 $obj.message_full_with_details(
1482 $crate::ElementMessageType::Error,
1483 $err,
1484 Some(&format!($($msg)*)),
1485 None,
1486 file!(),
1487 $crate::glib::function_name!(),
1488 line!(),
1489 $details,
1490 );
1491 }};
1492 ($obj:expr, $err:expr, [$($debug:tt)*], details: $details:expr) => { {
1493 use $crate::prelude::ElementExtManual;
1494 $obj.message_full_with_details(
1495 $crate::ElementMessageType::Error,
1496 $err,
1497 None,
1498 Some(&format!($($debug)*)),
1499 file!(),
1500 $crate::glib::function_name!(),
1501 line!(),
1502 $details,
1503 );
1504 }};
1505);
1506
1507#[doc(alias = "GST_ELEMENT_WARNING")]
1508#[doc(alias = "GST_ELEMENT_WARNING_WITH_DETAILS")]
1509#[macro_export]
1510macro_rules! element_warning(
1511 ($obj:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*]) => { {
1512 use $crate::prelude::ElementExtManual;
1513 $obj.message_full(
1514 $crate::ElementMessageType::Warning,
1515 $err,
1516 Some(&format!($($msg)*)),
1517 Some(&format!($($debug)*)),
1518 file!(),
1519 $crate::glib::function_name!(),
1520 line!(),
1521 );
1522 }};
1523 ($obj:expr, $err:expr, ($($msg:tt)*)) => { {
1524 use $crate::prelude::ElementExtManual;
1525 $obj.message_full(
1526 $crate::ElementMessageType::Warning,
1527 $err,
1528 Some(&format!($($msg)*)),
1529 None,
1530 file!(),
1531 $crate::glib::function_name!(),
1532 line!(),
1533 );
1534 }};
1535 ($obj:expr, $err:expr, [$($debug:tt)*]) => { {
1536 use $crate::prelude::ElementExtManual;
1537 $obj.message_full(
1538 $crate::ElementMessageType::Warning,
1539 $err,
1540 None,
1541 Some(&format!($($debug)*)),
1542 file!(),
1543 $crate::glib::function_name!(),
1544 line!(),
1545 );
1546 }};
1547
1548 ($obj:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*], details: $details:expr) => { {
1549 use $crate::prelude::ElementExtManual;
1550 $obj.message_full_with_details(
1551 $crate::ElementMessageType::Warning,
1552 $err,
1553 Some(&format!($($msg)*)),
1554 Some(&format!($($debug)*)),
1555 file!(),
1556 $crate::glib::function_name!(),
1557 line!(),
1558 $details,
1559 );
1560 }};
1561 ($obj:expr, $err:expr, ($($msg:tt)*), details: $details:expr) => { {
1562 use $crate::prelude::ElementExtManual;
1563 $obj.message_full_with_details(
1564 $crate::ElementMessageType::Warning,
1565 $err,
1566 Some(&format!($($msg)*)),
1567 None,
1568 file!(),
1569 $crate::glib::function_name!(),
1570 line!(),
1571 $details,
1572 );
1573 }};
1574 ($obj:expr, $err:expr, [$($debug:tt)*], details: $details:expr) => { {
1575 use $crate::prelude::ElementExtManual;
1576 $obj.message_full_with_details(
1577 $crate::ElementMessageType::Warning,
1578 $err,
1579 None,
1580 Some(&format!($($debug)*)),
1581 file!(),
1582 $crate::glib::function_name!(),
1583 line!(),
1584 $details,
1585 );
1586 }};
1587);
1588
1589#[doc(alias = "GST_ELEMENT_INFO")]
1590#[doc(alias = "GST_ELEMENT_INFO_WITH_DETAILS")]
1591#[macro_export]
1592macro_rules! element_info(
1593 ($obj:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*]) => { {
1594 use $crate::prelude::ElementExtManual;
1595 $obj.message_full(
1596 $crate::ElementMessageType::Info,
1597 $err,
1598 Some(&format!($($msg)*)),
1599 Some(&format!($($debug)*)),
1600 file!(),
1601 $crate::glib::function_name!(),
1602 line!(),
1603 );
1604 }};
1605 ($obj:expr, $err:expr, ($($msg:tt)*)) => { {
1606 use $crate::prelude::ElementExtManual;
1607 $obj.message_full(
1608 $crate::ElementMessageType::Info,
1609 $err,
1610 Some(&format!($($msg)*)),
1611 None,
1612 file!(),
1613 $crate::glib::function_name!(),
1614 line!(),
1615 );
1616 }};
1617 ($obj:expr, $err:expr, [$($debug:tt)*]) => { {
1618 use $crate::prelude::ElementExtManual;
1619 $obj.message_full(
1620 $crate::ElementMessageType::Info,
1621 $err,
1622 None,
1623 Some(&format!($($debug)*)),
1624 file!(),
1625 $crate::glib::function_name!(),
1626 line!(),
1627 );
1628 }};
1629
1630 ($obj:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*], details: $details:expr) => { {
1631 use $crate::prelude::ElementExtManual;
1632 $obj.message_full_with_details(
1633 $crate::ElementMessageType::Info,
1634 $err,
1635 Some(&format!($($msg)*)),
1636 Some(&format!($($debug)*)),
1637 file!(),
1638 $crate::glib::function_name!(),
1639 line!(),
1640 $details,
1641 );
1642 }};
1643 ($obj:expr, $err:expr, ($($msg:tt)*), details: $details:expr) => { {
1644 use $crate::prelude::ElementExtManual;
1645 $obj.message_full_with_details(
1646 $crate::ElementMessageType::Info,
1647 $err,
1648 Some(&format!($($msg)*)),
1649 None,
1650 file!(),
1651 $crate::glib::function_name!(),
1652 line!(),
1653 $details,
1654 );
1655 }};
1656 ($obj:expr, $err:expr, [$($debug:tt)*], details: $details:expr) => { {
1657 use $crate::prelude::ElementExtManual;
1658 $obj.message_full_with_details(
1659 $crate::ElementMessageType::Info,
1660 $err,
1661 None,
1662 Some(&format!($($debug)*)),
1663 file!(),
1664 $crate::glib::function_name!(),
1665 line!(),
1666 $details,
1667 );
1668 }};
1669);
1670
1671#[doc(alias = "GST_ELEMENT_ERROR")]
1672#[doc(alias = "GST_ELEMENT_ERROR_WITH_DETAILS")]
1673#[macro_export]
1674macro_rules! element_imp_error(
1675 ($imp:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*]) => { {
1676 let obj = $imp.obj();
1677 $crate::element_error!(obj, $err, ($($msg)*), [$($debug)*]);
1678 }};
1679 ($imp:expr, $err:expr, ($($msg:tt)*)) => { {
1680 let obj = $imp.obj();
1681 $crate::element_error!(obj, $err, ($($msg)*));
1682 }};
1683 ($imp:expr, $err:expr, [$($debug:tt)*]) => { {
1684 let obj = $imp.obj();
1685 $crate::element_error!(obj, $err, [$($debug)*]);
1686 }};
1687
1688 ($imp:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*], details: $details:expr) => { {
1689 let obj = $imp.obj();
1690 $crate::element_error!(obj, $err, ($($msg)*), [$($debug)*], details: $details);
1691 }};
1692 ($imp:expr, $err:expr, ($($msg:tt)*), details: $details:expr) => { {
1693 let obj = $imp.obj();
1694 $crate::element_error!(obj, $err, ($($msg)*), details: $details);
1695 }};
1696 ($imp:expr, $err:expr, [$($debug:tt)*], details: $details:expr) => { {
1697 let obj = $imp.obj();
1698 $crate::element_error!(obj, $err, [$($debug)*], details: $details);
1699 }};
1700);
1701
1702#[doc(alias = "GST_ELEMENT_WARNING")]
1703#[doc(alias = "GST_ELEMENT_WARNING_WITH_DETAILS")]
1704#[macro_export]
1705macro_rules! element_imp_warning(
1706 ($imp:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*]) => { {
1707 let obj = $imp.obj();
1708 $crate::element_warning!(obj, $err, ($($msg)*), [$($debug)*]);
1709 }};
1710 ($imp:expr, $err:expr, ($($msg:tt)*)) => { {
1711 let obj = $imp.obj();
1712 $crate::element_warning!(obj, $err, ($($msg)*));
1713 }};
1714 ($imp:expr, $err:expr, [$($debug:tt)*]) => { {
1715 let obj = $imp.obj();
1716 $crate::element_warning!(obj, $err, [$($debug)*]);
1717 }};
1718
1719 ($imp:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*], details: $details:expr) => { {
1720 let obj = $imp.obj();
1721 $crate::element_warning!(obj, $err, ($($msg)*), [$($debug)*], details: $details);
1722 }};
1723 ($imp:expr, $err:expr, ($($msg:tt)*), details: $details:expr) => { {
1724 let obj = $imp.obj();
1725 $crate::element_warning!(obj, $err, ($($msg)*), details: $details);
1726 }};
1727 ($imp:expr, $err:expr, [$($debug:tt)*], details: $details:expr) => { {
1728 let obj = $imp.obj();
1729 $crate::element_warning!(obj, $err, [$($debug)*], details: $details);
1730 }};
1731);
1732
1733#[doc(alias = "GST_ELEMENT_INFO")]
1734#[doc(alias = "GST_ELEMENT_INFO_WITH_DETAILS")]
1735#[macro_export]
1736macro_rules! element_imp_info(
1737 ($imp:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*]) => { {
1738 let obj = $imp.obj();
1739 $crate::element_info!(obj, $err, ($($msg)*), [$($debug)*]);
1740 }};
1741 ($imp:expr, $err:expr, ($($msg:tt)*)) => { {
1742 let obj = $imp.obj();
1743 $crate::element_info!(obj, $err, ($($msg)*));
1744 }};
1745 ($imp:expr, $err:expr, [$($debug:tt)*]) => { {
1746 let obj = $imp.obj();
1747 $crate::element_info!(obj, $err, [$($debug)*]);
1748 }};
1749
1750 ($imp:expr, $err:expr, ($($msg:tt)*), [$($debug:tt)*], details: $details:expr) => { {
1751 let obj = $imp.obj();
1752 $crate::element_info!(obj, $err, ($($msg)*), [$($debug)*], details: $details);
1753 }};
1754 ($imp:expr, $err:expr, ($($msg:tt)*), details: $details:expr) => { {
1755 let obj = $imp.obj();
1756 $crate::element_info!(obj, $err, ($($msg)*), details: $details);
1757 }};
1758 ($imp:expr, $err:expr, [$($debug:tt)*], details: $details:expr) => { {
1759 let obj = $imp.obj();
1760 $crate::element_info!(obj, $err, [$($debug)*], details: $details);
1761 }};
1762);
1763
1764#[cfg(test)]
1765mod tests {
1766 use std::sync::mpsc::channel;
1767
1768 use glib::GString;
1769
1770 use super::*;
1771
1772 #[test]
1773 fn test_get_pads() {
1774 crate::init().unwrap();
1775
1776 let identity = crate::ElementFactory::make("identity").build().unwrap();
1777
1778 let mut pad_names = identity
1779 .pads()
1780 .iter()
1781 .map(|p| p.name())
1782 .collect::<Vec<GString>>();
1783 pad_names.sort();
1784 assert_eq!(pad_names, vec![String::from("sink"), String::from("src")]);
1785
1786 let mut pad_names = identity
1787 .sink_pads()
1788 .iter()
1789 .map(|p| p.name())
1790 .collect::<Vec<GString>>();
1791 pad_names.sort();
1792 assert_eq!(pad_names, vec![String::from("sink")]);
1793
1794 let mut pad_names = identity
1795 .src_pads()
1796 .iter()
1797 .map(|p| p.name())
1798 .collect::<Vec<GString>>();
1799 pad_names.sort();
1800 assert_eq!(pad_names, vec![String::from("src")]);
1801 }
1802
1803 #[test]
1804 fn test_foreach_pad() {
1805 crate::init().unwrap();
1806
1807 let identity = crate::ElementFactory::make("identity").build().unwrap();
1808
1809 let mut pad_names = Vec::new();
1810 identity.foreach_pad(|_element, pad| {
1811 pad_names.push(pad.name());
1812
1813 ControlFlow::Continue(())
1814 });
1815 pad_names.sort();
1816 assert_eq!(pad_names, vec![String::from("sink"), String::from("src")]);
1817
1818 pad_names.clear();
1819 identity.foreach_sink_pad(|_element, pad| {
1820 pad_names.push(pad.name());
1821
1822 ControlFlow::Continue(())
1823 });
1824 assert_eq!(pad_names, vec![String::from("sink")]);
1825
1826 pad_names.clear();
1827 identity.foreach_src_pad(|_element, pad| {
1828 pad_names.push(pad.name());
1829
1830 ControlFlow::Continue(())
1831 });
1832 assert_eq!(pad_names, vec![String::from("src")]);
1833 }
1834
1835 #[test]
1836 fn test_call_async() {
1837 crate::init().unwrap();
1838
1839 let identity = crate::ElementFactory::make("identity").build().unwrap();
1840 let (sender, receiver) = channel();
1841
1842 identity.call_async(move |_| {
1843 sender.send(()).unwrap();
1844 });
1845
1846 assert_eq!(receiver.recv(), Ok(()));
1847 }
1848
1849 #[test]
1850 fn test_element_error() {
1851 crate::init().unwrap();
1852
1853 let identity = crate::ElementFactory::make("identity").build().unwrap();
1854
1855 crate::element_error!(identity, crate::CoreError::Failed, ("msg"), ["debug"]);
1856 crate::element_error!(identity, crate::CoreError::Failed, ["debug"]);
1857 crate::element_error!(identity, crate::CoreError::Failed, ("msg"));
1858
1859 let x = 123i32;
1862 crate::element_error!(identity, crate::CoreError::Failed, ("msg {x}"), ["debug"]);
1863 let x = 123i32;
1864 crate::element_error!(identity, crate::CoreError::Failed, ["debug {x}"]);
1865 let x = 123i32;
1866 crate::element_error!(identity, crate::CoreError::Failed, ("msg {}", x));
1867 }
1868}