Skip to main content

gstreamer/
caps_features.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{
4    borrow::{Borrow, BorrowMut, ToOwned},
5    fmt,
6    marker::PhantomData,
7    mem,
8    ops::{Deref, DerefMut},
9    ptr, str,
10};
11
12use crate::{IdStr, ffi};
13use cfg_if::cfg_if;
14use glib::{prelude::*, translate::*};
15use std::sync::LazyLock;
16
17/// [`CapsFeatures`][crate::CapsFeatures] can optionally be set on a [`Caps`][crate::Caps] to add requirements
18/// for additional features for a specific [`Structure`][crate::Structure]. Caps structures with
19/// the same name but with a non-equal set of caps features are not compatible.
20/// If a pad supports multiple sets of features it has to add multiple equal
21/// structures with different feature sets to the caps.
22///
23/// Empty [`CapsFeatures`][crate::CapsFeatures] are equivalent with the [`CapsFeatures`][crate::CapsFeatures] that only
24/// contain `GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY`. ANY [`CapsFeatures`][crate::CapsFeatures] as
25/// created by [`new_any()`][Self::new_any()] are equal to any other [`CapsFeatures`][crate::CapsFeatures]
26/// and can be used to specify that any [`CapsFeatures`][crate::CapsFeatures] would be supported, e.g.
27/// for elements that don't touch buffer memory. [`Caps`][crate::Caps] with ANY [`CapsFeatures`][crate::CapsFeatures]
28/// are considered non-fixed and during negotiation some [`CapsFeatures`][crate::CapsFeatures] have
29/// to be selected.
30///
31/// Examples for caps features would be the requirement of a specific [`Memory`][crate::Memory]
32/// types or the requirement of having a specific `GstMeta` on the buffer. Features
33/// are given as a string of the format `memory:GstMemoryTypeName` or
34/// `meta:GstMetaAPIName`.
35#[doc(alias = "GstCapsFeatures")]
36#[repr(transparent)]
37pub struct CapsFeatures(ptr::NonNull<ffi::GstCapsFeatures>);
38unsafe impl Send for CapsFeatures {}
39unsafe impl Sync for CapsFeatures {}
40
41impl CapsFeatures {
42    /// Creates a new [`CapsFeatures`][crate::CapsFeatures] with the given features.
43    /// The last argument must be [`None`].
44    /// ## `feature1`
45    /// name of first feature to set
46    ///
47    /// # Returns
48    ///
49    /// a new, empty [`CapsFeatures`][crate::CapsFeatures]
50    #[doc(alias = "gst_caps_features_new")]
51    pub fn new<S: IntoGStr>(features: impl IntoIterator<Item = S>) -> Self {
52        skip_assert_initialized!();
53        let mut f = Self::new_empty();
54
55        for feature in features {
56            f.add(feature);
57        }
58
59        f
60    }
61
62    #[doc(alias = "gst_caps_features_new_static_str")]
63    pub fn new_from_static<S: AsRef<glib::GStr> + 'static>(
64        features: impl IntoIterator<Item = S>,
65    ) -> Self {
66        skip_assert_initialized!();
67        let mut f = Self::new_empty();
68
69        for feature in features {
70            f.add_from_static(feature);
71        }
72
73        f
74    }
75
76    #[doc(alias = "gst_caps_features_new_id_str")]
77    pub fn new_from_id<S: AsRef<IdStr>>(features: impl IntoIterator<Item = S>) -> Self {
78        skip_assert_initialized!();
79        let mut f = Self::new_empty();
80
81        for feature in features {
82            f.add_from_id(feature);
83        }
84
85        f
86    }
87
88    #[deprecated = "use `new_by_id()` instead"]
89    #[allow(deprecated)]
90    #[doc(alias = "gst_caps_features_new_id")]
91    pub fn from_quarks(features: impl IntoIterator<Item = glib::Quark>) -> Self {
92        skip_assert_initialized!();
93        let mut f = Self::new_empty();
94
95        for feature in features.into_iter() {
96            f.add_from_quark(feature);
97        }
98
99        f
100    }
101
102    /// Creates a new, empty [`CapsFeatures`][crate::CapsFeatures].
103    ///
104    /// # Returns
105    ///
106    /// a new, empty [`CapsFeatures`][crate::CapsFeatures]
107    #[doc(alias = "gst_caps_features_new_empty")]
108    pub fn new_empty() -> Self {
109        assert_initialized_main_thread!();
110        unsafe {
111            CapsFeatures(ptr::NonNull::new_unchecked(
112                ffi::gst_caps_features_new_empty(),
113            ))
114        }
115    }
116
117    /// Creates a new, ANY [`CapsFeatures`][crate::CapsFeatures]. This will be equal
118    /// to any other [`CapsFeatures`][crate::CapsFeatures] but caps with these are
119    /// unfixed.
120    ///
121    /// # Returns
122    ///
123    /// a new, ANY [`CapsFeatures`][crate::CapsFeatures]
124    #[doc(alias = "gst_caps_features_new_any")]
125    pub fn new_any() -> Self {
126        assert_initialized_main_thread!();
127        unsafe { CapsFeatures(ptr::NonNull::new_unchecked(ffi::gst_caps_features_new_any())) }
128    }
129}
130
131impl IntoGlibPtr<*mut ffi::GstCapsFeatures> for CapsFeatures {
132    #[inline]
133    fn into_glib_ptr(self) -> *mut ffi::GstCapsFeatures {
134        let s = mem::ManuallyDrop::new(self);
135        s.0.as_ptr()
136    }
137}
138
139impl Deref for CapsFeatures {
140    type Target = CapsFeaturesRef;
141
142    #[inline]
143    fn deref(&self) -> &CapsFeaturesRef {
144        unsafe { &*(self.0.as_ref() as *const ffi::GstCapsFeatures as *const CapsFeaturesRef) }
145    }
146}
147
148impl DerefMut for CapsFeatures {
149    #[inline]
150    fn deref_mut(&mut self) -> &mut CapsFeaturesRef {
151        unsafe { &mut *(self.0.as_mut() as *mut ffi::GstCapsFeatures as *mut CapsFeaturesRef) }
152    }
153}
154
155impl AsRef<CapsFeaturesRef> for CapsFeatures {
156    #[inline]
157    fn as_ref(&self) -> &CapsFeaturesRef {
158        self.deref()
159    }
160}
161
162impl AsMut<CapsFeaturesRef> for CapsFeatures {
163    #[inline]
164    fn as_mut(&mut self) -> &mut CapsFeaturesRef {
165        self.deref_mut()
166    }
167}
168
169impl Clone for CapsFeatures {
170    #[inline]
171    fn clone(&self) -> Self {
172        unsafe {
173            let ptr = ffi::gst_caps_features_copy(self.0.as_ref());
174            debug_assert!(!ptr.is_null());
175            CapsFeatures(ptr::NonNull::new_unchecked(ptr))
176        }
177    }
178}
179
180impl Drop for CapsFeatures {
181    #[inline]
182    fn drop(&mut self) {
183        unsafe { ffi::gst_caps_features_free(self.0.as_mut()) }
184    }
185}
186
187impl fmt::Debug for CapsFeatures {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        f.debug_tuple("CapsFeatures")
190            .field(&self.to_string())
191            .finish()
192    }
193}
194
195impl fmt::Display for CapsFeatures {
196    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197        // Need to make sure to not call ToString::to_string() here, which
198        // we have because of the Display impl. We need CapsFeaturesRef::to_string()
199        f.write_str(&CapsFeaturesRef::to_string(self.as_ref()))
200    }
201}
202
203impl str::FromStr for CapsFeatures {
204    type Err = glib::BoolError;
205
206    #[doc(alias = "gst_caps_features_from_string")]
207    fn from_str(s: &str) -> Result<Self, Self::Err> {
208        assert_initialized_main_thread!();
209        unsafe {
210            let ptr = s.run_with_gstr(|s| ffi::gst_caps_features_from_string(s.as_ptr()));
211            if ptr.is_null() {
212                return Err(glib::bool_error!(
213                    "Failed to parse caps features from string"
214                ));
215            }
216
217            Ok(Self(ptr::NonNull::new_unchecked(ptr)))
218        }
219    }
220}
221
222impl Borrow<CapsFeaturesRef> for CapsFeatures {
223    #[inline]
224    fn borrow(&self) -> &CapsFeaturesRef {
225        self.as_ref()
226    }
227}
228
229impl BorrowMut<CapsFeaturesRef> for CapsFeatures {
230    #[inline]
231    fn borrow_mut(&mut self) -> &mut CapsFeaturesRef {
232        self.as_mut()
233    }
234}
235
236impl glib::types::StaticType for CapsFeatures {
237    #[inline]
238    fn static_type() -> glib::types::Type {
239        unsafe { from_glib(ffi::gst_caps_features_get_type()) }
240    }
241}
242
243impl<'a> ToGlibPtr<'a, *const ffi::GstCapsFeatures> for CapsFeatures {
244    type Storage = PhantomData<&'a Self>;
245
246    #[inline]
247    fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GstCapsFeatures, Self> {
248        unsafe { Stash(self.0.as_ref(), PhantomData) }
249    }
250
251    #[inline]
252    fn to_glib_full(&self) -> *const ffi::GstCapsFeatures {
253        unsafe { ffi::gst_caps_features_copy(self.0.as_ref()) }
254    }
255}
256
257impl<'a> ToGlibPtr<'a, *mut ffi::GstCapsFeatures> for CapsFeatures {
258    type Storage = PhantomData<&'a Self>;
259
260    #[inline]
261    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GstCapsFeatures, Self> {
262        unsafe {
263            Stash(
264                self.0.as_ref() as *const ffi::GstCapsFeatures as *mut ffi::GstCapsFeatures,
265                PhantomData,
266            )
267        }
268    }
269
270    #[inline]
271    fn to_glib_full(&self) -> *mut ffi::GstCapsFeatures {
272        unsafe { ffi::gst_caps_features_copy(self.0.as_ref()) }
273    }
274}
275
276impl<'a> ToGlibPtrMut<'a, *mut ffi::GstCapsFeatures> for CapsFeatures {
277    type Storage = PhantomData<&'a mut Self>;
278
279    #[inline]
280    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GstCapsFeatures, Self> {
281        unsafe { StashMut(self.0.as_mut(), PhantomData) }
282    }
283}
284
285impl FromGlibPtrNone<*const ffi::GstCapsFeatures> for CapsFeatures {
286    #[inline]
287    unsafe fn from_glib_none(ptr: *const ffi::GstCapsFeatures) -> Self {
288        unsafe {
289            debug_assert!(!ptr.is_null());
290            let ptr = ffi::gst_caps_features_copy(ptr);
291            debug_assert!(!ptr.is_null());
292            CapsFeatures(ptr::NonNull::new_unchecked(ptr))
293        }
294    }
295}
296
297impl FromGlibPtrNone<*mut ffi::GstCapsFeatures> for CapsFeatures {
298    #[inline]
299    unsafe fn from_glib_none(ptr: *mut ffi::GstCapsFeatures) -> Self {
300        unsafe {
301            debug_assert!(!ptr.is_null());
302            let ptr = ffi::gst_caps_features_copy(ptr);
303            debug_assert!(!ptr.is_null());
304            CapsFeatures(ptr::NonNull::new_unchecked(ptr))
305        }
306    }
307}
308
309impl FromGlibPtrFull<*const ffi::GstCapsFeatures> for CapsFeatures {
310    #[inline]
311    unsafe fn from_glib_full(ptr: *const ffi::GstCapsFeatures) -> Self {
312        unsafe {
313            debug_assert!(!ptr.is_null());
314            CapsFeatures(ptr::NonNull::new_unchecked(
315                ptr as *mut ffi::GstCapsFeatures,
316            ))
317        }
318    }
319}
320
321impl FromGlibPtrFull<*mut ffi::GstCapsFeatures> for CapsFeatures {
322    #[inline]
323    unsafe fn from_glib_full(ptr: *mut ffi::GstCapsFeatures) -> Self {
324        unsafe {
325            debug_assert!(!ptr.is_null());
326            CapsFeatures(ptr::NonNull::new_unchecked(ptr))
327        }
328    }
329}
330
331impl glib::value::ValueType for CapsFeatures {
332    type Type = Self;
333}
334
335impl glib::value::ValueTypeOptional for CapsFeatures {}
336
337unsafe impl<'a> glib::value::FromValue<'a> for CapsFeatures {
338    type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
339
340    unsafe fn from_value(value: &'a glib::Value) -> Self {
341        unsafe {
342            skip_assert_initialized!();
343            from_glib_none(glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0)
344                as *mut ffi::GstCapsFeatures)
345        }
346    }
347}
348
349impl glib::value::ToValue for CapsFeatures {
350    fn to_value(&self) -> glib::Value {
351        let mut value = glib::Value::for_value_type::<Self>();
352        unsafe {
353            glib::gobject_ffi::g_value_set_boxed(
354                value.to_glib_none_mut().0,
355                ToGlibPtr::<*mut ffi::GstCapsFeatures>::to_glib_none(self).0 as *mut _,
356            )
357        }
358        value
359    }
360
361    fn value_type(&self) -> glib::Type {
362        Self::static_type()
363    }
364}
365
366impl glib::value::ToValueOptional for CapsFeatures {
367    fn to_value_optional(s: Option<&Self>) -> glib::Value {
368        skip_assert_initialized!();
369        let mut value = glib::Value::for_value_type::<Self>();
370        unsafe {
371            glib::gobject_ffi::g_value_set_boxed(
372                value.to_glib_none_mut().0,
373                ToGlibPtr::<*mut ffi::GstCapsFeatures>::to_glib_none(&s).0 as *mut _,
374            )
375        }
376        value
377    }
378}
379
380impl From<CapsFeatures> for glib::Value {
381    fn from(v: CapsFeatures) -> glib::Value {
382        skip_assert_initialized!();
383        let mut value = glib::Value::for_value_type::<CapsFeatures>();
384        unsafe {
385            glib::gobject_ffi::g_value_take_boxed(
386                value.to_glib_none_mut().0,
387                IntoGlibPtr::<*mut ffi::GstCapsFeatures>::into_glib_ptr(v) as *mut _,
388            )
389        }
390        value
391    }
392}
393
394impl GlibPtrDefault for CapsFeatures {
395    type GlibType = *mut ffi::GstCapsFeatures;
396}
397
398unsafe impl TransparentPtrType for CapsFeatures {}
399
400#[repr(transparent)]
401#[doc(alias = "GstCapsFeatures")]
402pub struct CapsFeaturesRef(ffi::GstCapsFeatures);
403
404impl CapsFeaturesRef {
405    #[inline]
406    pub unsafe fn from_glib_borrow<'a>(ptr: *const ffi::GstCapsFeatures) -> &'a CapsFeaturesRef {
407        unsafe {
408            debug_assert!(!ptr.is_null());
409
410            &*(ptr as *mut CapsFeaturesRef)
411        }
412    }
413
414    #[inline]
415    pub unsafe fn from_glib_borrow_mut<'a>(
416        ptr: *mut ffi::GstCapsFeatures,
417    ) -> &'a mut CapsFeaturesRef {
418        unsafe {
419            debug_assert!(!ptr.is_null());
420
421            &mut *(ptr as *mut CapsFeaturesRef)
422        }
423    }
424
425    #[inline]
426    pub fn as_ptr(&self) -> *const ffi::GstCapsFeatures {
427        self as *const Self as *const ffi::GstCapsFeatures
428    }
429
430    #[inline]
431    pub fn as_mut_ptr(&self) -> *mut ffi::GstCapsFeatures {
432        self as *const Self as *mut ffi::GstCapsFeatures
433    }
434
435    pub fn is_empty(&self) -> bool {
436        self.size() == 0 && !self.is_any()
437    }
438
439    #[doc(alias = "gst_caps_features_is_any")]
440    pub fn is_any(&self) -> bool {
441        unsafe { from_glib(ffi::gst_caps_features_is_any(self.as_ptr())) }
442    }
443
444    #[doc(alias = "gst_caps_features_contains")]
445    pub fn contains(&self, feature: impl IntoGStr) -> bool {
446        unsafe {
447            feature.run_with_gstr(|feature| {
448                from_glib(ffi::gst_caps_features_contains(
449                    self.as_ptr(),
450                    feature.as_ptr(),
451                ))
452            })
453        }
454    }
455
456    #[doc(alias = "gst_caps_features_contains_id_str")]
457    pub fn contains_by_id(&self, feature: impl AsRef<IdStr>) -> bool {
458        unsafe {
459            cfg_if! {
460                if #[cfg(feature = "v1_26")] {
461                    from_glib(ffi::gst_caps_features_contains_id_str(
462                        self.as_ptr(),
463                        feature.as_ref().as_ptr(),
464                    ))
465                } else {
466                    from_glib(ffi::gst_caps_features_contains(
467                        self.as_ptr(),
468                        feature.as_ref().as_gstr().as_ptr(),
469                    ))
470                }
471            }
472        }
473    }
474
475    #[deprecated = "use `contains_by_id()` instead"]
476    #[doc(alias = "gst_caps_features_contains_id")]
477    pub fn contains_quark(&self, feature: glib::Quark) -> bool {
478        unsafe {
479            from_glib(ffi::gst_caps_features_contains_id(
480                self.as_ptr(),
481                feature.into_glib(),
482            ))
483        }
484    }
485
486    #[doc(alias = "get_size")]
487    #[doc(alias = "gst_caps_features_get_size")]
488    pub fn size(&self) -> usize {
489        unsafe { ffi::gst_caps_features_get_size(self.as_ptr()) as usize }
490    }
491
492    #[doc(alias = "get_nth")]
493    #[doc(alias = "gst_caps_features_get_nth")]
494    pub fn nth(&self, idx: usize) -> Option<&glib::GStr> {
495        if idx >= self.size() {
496            return None;
497        }
498
499        unsafe {
500            let feature = ffi::gst_caps_features_get_nth(self.as_ptr(), idx as u32);
501            if feature.is_null() {
502                return None;
503            }
504
505            Some(glib::GStr::from_ptr(feature))
506        }
507    }
508
509    #[cfg(feature = "v1_26")]
510    #[doc(alias = "get_nth_by_id")]
511    #[doc(alias = "gst_caps_features_get_nth_id_str")]
512    pub fn nth_id(&self, idx: usize) -> Option<&IdStr> {
513        if idx >= self.size() {
514            return None;
515        }
516
517        unsafe {
518            let feature = ffi::gst_caps_features_get_nth_id_str(self.as_ptr(), idx as u32);
519            if feature.is_null() {
520                return None;
521            }
522
523            Some(&*(feature as *const IdStr))
524        }
525    }
526
527    #[deprecated = "use `nth_by_id()` instead"]
528    #[doc(alias = "gst_caps_features_get_nth_id")]
529    pub fn nth_quark(&self, idx: usize) -> Option<glib::Quark> {
530        if idx >= self.size() {
531            return None;
532        }
533
534        unsafe {
535            let feature = ffi::gst_caps_features_get_nth_id(self.as_ptr(), idx as u32);
536            Some(from_glib(feature))
537        }
538    }
539
540    #[doc(alias = "gst_caps_features_add")]
541    pub fn add(&mut self, feature: impl IntoGStr) {
542        unsafe {
543            feature.run_with_gstr(|feature| {
544                ffi::gst_caps_features_add(self.as_mut_ptr(), feature.as_ptr())
545            })
546        }
547    }
548
549    #[doc(alias = "gst_caps_features_add_static_str")]
550    pub fn add_from_static(&mut self, feature: impl AsRef<glib::GStr> + 'static) {
551        unsafe {
552            cfg_if! {
553                if #[cfg(feature = "v1_26")] {
554                    ffi::gst_caps_features_add_static_str(self.as_mut_ptr(), feature.as_ref().as_ptr())
555                } else {
556                    ffi::gst_caps_features_add(self.as_mut_ptr(), feature.as_ref().as_ptr())
557                }
558            }
559        }
560    }
561
562    #[doc(alias = "gst_caps_features_add_id_str")]
563    pub fn add_from_id(&mut self, feature: impl AsRef<IdStr>) {
564        unsafe {
565            cfg_if! {
566                if #[cfg(feature = "v1_26")] {
567                    ffi::gst_caps_features_add_id_str(self.as_mut_ptr(), feature.as_ref().as_ptr())
568                } else {
569                    ffi::gst_caps_features_add(self.as_mut_ptr(), feature.as_ref().as_gstr().as_ptr())
570                }
571            }
572        }
573    }
574
575    #[doc(alias = "gst_caps_features_remove")]
576    pub fn remove(&mut self, feature: impl IntoGStr) {
577        unsafe {
578            feature.run_with_gstr(|feature| {
579                ffi::gst_caps_features_remove(self.as_mut_ptr(), feature.as_ptr())
580            })
581        }
582    }
583
584    #[doc(alias = "gst_caps_features_remove_id_str")]
585    pub fn remove_by_id(&mut self, feature: impl AsRef<IdStr>) {
586        unsafe {
587            cfg_if! {
588                if #[cfg(feature = "v1_26")] {
589                    ffi::gst_caps_features_remove_id_str(self.as_mut_ptr(), feature.as_ref().as_ptr())
590                } else {
591                    ffi::gst_caps_features_remove(self.as_mut_ptr(), feature.as_ref().as_gstr().as_ptr())
592                }
593            }
594        }
595    }
596
597    #[deprecated = "use `add_by_id()` instead"]
598    #[doc(alias = "gst_caps_features_add_id")]
599    pub fn add_from_quark(&mut self, feature: glib::Quark) {
600        unsafe { ffi::gst_caps_features_add_id(self.as_mut_ptr(), feature.into_glib()) }
601    }
602
603    #[deprecated = "use `remove_by_id()` instead"]
604    #[doc(alias = "gst_caps_features_remove_id")]
605    pub fn remove_by_quark(&mut self, feature: glib::Quark) {
606        unsafe { ffi::gst_caps_features_remove_id(self.as_mut_ptr(), feature.into_glib()) }
607    }
608
609    pub fn iter(&self) -> Iter<'_> {
610        Iter::new(self)
611    }
612
613    // This is not an equivalence relation with regards to ANY. Everything is equal to ANY
614    #[doc(alias = "gst_caps_features_is_equal")]
615    pub fn is_equal(&self, other: &CapsFeaturesRef) -> bool {
616        unsafe {
617            from_glib(ffi::gst_caps_features_is_equal(
618                self.as_ptr(),
619                other.as_ptr(),
620            ))
621        }
622    }
623}
624
625impl glib::types::StaticType for CapsFeaturesRef {
626    #[inline]
627    fn static_type() -> glib::types::Type {
628        unsafe { from_glib(ffi::gst_structure_get_type()) }
629    }
630}
631
632impl<'a> std::iter::Extend<&'a str> for CapsFeaturesRef {
633    fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
634        iter.into_iter().for_each(|f| self.add(f));
635    }
636}
637
638impl<'a> std::iter::Extend<&'a glib::GStr> for CapsFeaturesRef {
639    fn extend<T: IntoIterator<Item = &'a glib::GStr>>(&mut self, iter: T) {
640        iter.into_iter().for_each(|f| self.add(f));
641    }
642}
643
644impl std::iter::Extend<String> for CapsFeaturesRef {
645    fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
646        iter.into_iter().for_each(|f| self.add(&f));
647    }
648}
649
650impl std::iter::Extend<glib::GString> for CapsFeaturesRef {
651    fn extend<T: IntoIterator<Item = glib::GString>>(&mut self, iter: T) {
652        iter.into_iter().for_each(|f| self.add(&f));
653    }
654}
655
656impl<Id: AsRef<IdStr>> std::iter::Extend<Id> for CapsFeaturesRef {
657    fn extend<T: IntoIterator<Item = Id>>(&mut self, iter: T) {
658        iter.into_iter().for_each(|f| self.add_from_id(f));
659    }
660}
661
662impl std::iter::Extend<glib::Quark> for CapsFeaturesRef {
663    #[allow(deprecated)]
664    fn extend<T: IntoIterator<Item = glib::Quark>>(&mut self, iter: T) {
665        iter.into_iter().for_each(|f| self.add_from_quark(f));
666    }
667}
668
669unsafe impl<'a> glib::value::FromValue<'a> for &'a CapsFeaturesRef {
670    type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
671
672    unsafe fn from_value(value: &'a glib::Value) -> Self {
673        unsafe {
674            skip_assert_initialized!();
675            &*(glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0)
676                as *const CapsFeaturesRef)
677        }
678    }
679}
680
681impl glib::value::ToValue for CapsFeaturesRef {
682    fn to_value(&self) -> glib::Value {
683        let mut value = glib::Value::for_value_type::<CapsFeatures>();
684        unsafe {
685            glib::gobject_ffi::g_value_set_boxed(
686                value.to_glib_none_mut().0,
687                self.as_mut_ptr() as *mut _,
688            )
689        }
690        value
691    }
692
693    fn value_type(&self) -> glib::Type {
694        Self::static_type()
695    }
696}
697
698impl glib::value::ToValueOptional for CapsFeaturesRef {
699    fn to_value_optional(s: Option<&Self>) -> glib::Value {
700        skip_assert_initialized!();
701        let mut value = glib::Value::for_value_type::<CapsFeatures>();
702        unsafe {
703            glib::gobject_ffi::g_value_set_boxed(
704                value.to_glib_none_mut().0,
705                s.map(|s| s.as_mut_ptr()).unwrap_or(ptr::null_mut()) as *mut _,
706            )
707        }
708        value
709    }
710}
711
712crate::utils::define_fixed_size_iter!(
713    Iter,
714    &'a CapsFeaturesRef,
715    &'a glib::GStr,
716    |collection: &CapsFeaturesRef| collection.size(),
717    |collection: &CapsFeaturesRef, idx: usize| unsafe {
718        let feature = ffi::gst_caps_features_get_nth(collection.as_ptr(), idx as u32);
719        glib::GStr::from_ptr(feature)
720    }
721);
722
723impl<'a> IntoIterator for &'a CapsFeaturesRef {
724    type IntoIter = Iter<'a>;
725    type Item = &'a glib::GStr;
726
727    fn into_iter(self) -> Self::IntoIter {
728        self.iter()
729    }
730}
731
732impl<'a> From<&'a str> for CapsFeatures {
733    fn from(value: &'a str) -> Self {
734        skip_assert_initialized!();
735        let mut features = CapsFeatures::new_empty();
736
737        features.add(value);
738
739        features
740    }
741}
742
743impl<'a> From<&'a glib::GStr> for CapsFeatures {
744    fn from(value: &'a glib::GStr) -> Self {
745        skip_assert_initialized!();
746        let mut features = CapsFeatures::new_empty();
747
748        features.add(value);
749
750        features
751    }
752}
753
754impl<Id: AsRef<IdStr>> From<Id> for CapsFeatures {
755    fn from(value: Id) -> Self {
756        skip_assert_initialized!();
757        let mut features = CapsFeatures::new_empty();
758
759        features.add_from_id(value);
760
761        features
762    }
763}
764
765impl From<glib::Quark> for CapsFeatures {
766    #[allow(deprecated)]
767    fn from(value: glib::Quark) -> Self {
768        skip_assert_initialized!();
769        let mut features = CapsFeatures::new_empty();
770
771        features.add_from_quark(value);
772
773        features
774    }
775}
776
777impl<'a, const N: usize> From<[&'a str; N]> for CapsFeatures {
778    fn from(value: [&'a str; N]) -> Self {
779        skip_assert_initialized!();
780        let mut features = CapsFeatures::new_empty();
781
782        value.into_iter().for_each(|f| features.add(f));
783
784        features
785    }
786}
787
788impl<'a, const N: usize> From<[&'a glib::GStr; N]> for CapsFeatures {
789    fn from(value: [&'a glib::GStr; N]) -> Self {
790        skip_assert_initialized!();
791        let mut features = CapsFeatures::new_empty();
792
793        value.into_iter().for_each(|f| features.add(f));
794
795        features
796    }
797}
798
799impl<const N: usize> From<[String; N]> for CapsFeatures {
800    fn from(value: [String; N]) -> Self {
801        skip_assert_initialized!();
802        let mut features = CapsFeatures::new_empty();
803
804        value.into_iter().for_each(|f| features.add(&f));
805
806        features
807    }
808}
809
810impl<const N: usize> From<[glib::GString; N]> for CapsFeatures {
811    fn from(value: [glib::GString; N]) -> Self {
812        skip_assert_initialized!();
813        let mut features = CapsFeatures::new_empty();
814
815        value.into_iter().for_each(|f| features.add(&f));
816
817        features
818    }
819}
820
821impl<const N: usize, Id: AsRef<IdStr>> From<[Id; N]> for CapsFeatures {
822    fn from(value: [Id; N]) -> Self {
823        skip_assert_initialized!();
824        let mut features = CapsFeatures::new_empty();
825
826        value.into_iter().for_each(|f| features.add_from_id(f));
827
828        features
829    }
830}
831
832impl<const N: usize> From<[glib::Quark; N]> for CapsFeatures {
833    #[allow(deprecated)]
834    fn from(value: [glib::Quark; N]) -> Self {
835        skip_assert_initialized!();
836        let mut features = CapsFeatures::new_empty();
837
838        value.into_iter().for_each(|f| features.add_from_quark(f));
839
840        features
841    }
842}
843
844impl<'a> std::iter::FromIterator<&'a str> for CapsFeatures {
845    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
846        skip_assert_initialized!();
847        let mut features = CapsFeatures::new_empty();
848
849        iter.into_iter().for_each(|f| features.add(f));
850
851        features
852    }
853}
854
855impl<'a> std::iter::FromIterator<&'a glib::GStr> for CapsFeatures {
856    fn from_iter<T: IntoIterator<Item = &'a glib::GStr>>(iter: T) -> Self {
857        assert_initialized_main_thread!();
858
859        let mut features = CapsFeatures::new_empty();
860
861        iter.into_iter().for_each(|f| features.add(f));
862
863        features
864    }
865}
866
867impl std::iter::FromIterator<String> for CapsFeatures {
868    fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
869        skip_assert_initialized!();
870        let mut features = CapsFeatures::new_empty();
871
872        iter.into_iter().for_each(|f| features.add(&f));
873
874        features
875    }
876}
877
878impl std::iter::FromIterator<glib::GString> for CapsFeatures {
879    fn from_iter<T: IntoIterator<Item = glib::GString>>(iter: T) -> Self {
880        assert_initialized_main_thread!();
881
882        let mut features = CapsFeatures::new_empty();
883
884        iter.into_iter().for_each(|f| features.add(&f));
885
886        features
887    }
888}
889
890impl<Id: AsRef<IdStr>> std::iter::FromIterator<Id> for CapsFeatures {
891    #[allow(deprecated)]
892    fn from_iter<T: IntoIterator<Item = Id>>(iter: T) -> Self {
893        skip_assert_initialized!();
894        let mut features = CapsFeatures::new_empty();
895
896        iter.into_iter().for_each(|f| features.add_from_id(f));
897
898        features
899    }
900}
901
902impl std::iter::FromIterator<glib::Quark> for CapsFeatures {
903    #[allow(deprecated)]
904    fn from_iter<T: IntoIterator<Item = glib::Quark>>(iter: T) -> Self {
905        skip_assert_initialized!();
906        let mut features = CapsFeatures::new_empty();
907
908        iter.into_iter().for_each(|f| features.add_from_quark(f));
909
910        features
911    }
912}
913
914impl fmt::Debug for CapsFeaturesRef {
915    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
916        f.debug_tuple("CapsFeatures")
917            .field(&self.to_string())
918            .finish()
919    }
920}
921
922impl fmt::Display for CapsFeaturesRef {
923    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
924        let s = unsafe {
925            glib::GString::from_glib_full(ffi::gst_caps_features_to_string(self.as_ptr()))
926        };
927        f.write_str(&s)
928    }
929}
930
931impl ToOwned for CapsFeaturesRef {
932    type Owned = CapsFeatures;
933
934    #[inline]
935    fn to_owned(&self) -> CapsFeatures {
936        unsafe { from_glib_full(ffi::gst_caps_features_copy(self.as_ptr() as *const _) as *mut _) }
937    }
938}
939
940impl std::hash::Hash for CapsFeaturesRef {
941    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
942        use std::hash::{DefaultHasher, Hasher};
943
944        // re-implement gst_hash_caps_features so the hashing is not depending on the features order.
945        if self.is_any() {
946            "ANY".hash(state);
947        } else if self.is_empty() {
948            "EMPTY".hash(state);
949        } else {
950            let mut features_hash = 0;
951            for f in self.iter() {
952                let mut field_hasher = DefaultHasher::new();
953                f.hash(&mut field_hasher);
954
955                features_hash ^= field_hasher.finish();
956            }
957            features_hash.hash(state);
958        }
959    }
960}
961
962impl std::hash::Hash for CapsFeatures {
963    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
964        self.as_ref().hash(state);
965    }
966}
967
968unsafe impl Sync for CapsFeaturesRef {}
969unsafe impl Send for CapsFeaturesRef {}
970
971pub static CAPS_FEATURE_MEMORY_SYSTEM_MEMORY: &glib::GStr =
972    unsafe { glib::GStr::from_utf8_with_nul_unchecked(ffi::GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) };
973pub static CAPS_FEATURES_MEMORY_SYSTEM_MEMORY: LazyLock<CapsFeatures> =
974    LazyLock::new(|| CapsFeatures::new([CAPS_FEATURE_MEMORY_SYSTEM_MEMORY]));
975
976#[cfg(test)]
977mod tests {
978    use super::*;
979    use glib::gstr;
980
981    #[test]
982    fn test_from_value_optional() {
983        use glib::value::ToValue;
984
985        crate::init().unwrap();
986
987        let a = None::<CapsFeatures>.to_value();
988        assert!(a.get::<Option<CapsFeatures>>().unwrap().is_none());
989        let b = glib::value::Value::from(&CapsFeatures::new_empty());
990        assert!(b.get::<Option<CapsFeatures>>().unwrap().is_some());
991    }
992
993    #[test]
994    fn trait_impls() {
995        crate::init().unwrap();
996
997        let cf = CapsFeatures::from(gstr!("memory:DMABuf"));
998        assert!(cf.contains(gstr!("memory:DMABuf")));
999
1000        let cf = CapsFeatures::from([
1001            gstr!("memory:DMABuf"),
1002            gstr!("meta:GstVideoOverlayComposition"),
1003        ]);
1004
1005        assert!(cf.contains(gstr!("memory:DMABuf")));
1006        assert!(cf.contains("meta:GstVideoOverlayComposition"));
1007        assert!(!cf.contains("memory:GLMemory"));
1008
1009        let cf = CapsFeatures::from_iter(vec![
1010            gstr!("memory:DMABuf"),
1011            gstr!("meta:GstVideoOverlayComposition"),
1012        ]);
1013
1014        assert!(cf.contains(gstr!("memory:DMABuf")));
1015        assert!(cf.contains("meta:GstVideoOverlayComposition"));
1016        assert!(!cf.contains("memory:GLMemory"));
1017
1018        let mut cf = CapsFeatures::new_empty();
1019        cf.extend([
1020            gstr!("memory:DMABuf"),
1021            gstr!("meta:GstVideoOverlayComposition"),
1022        ]);
1023
1024        assert!(cf.contains(gstr!("memory:DMABuf")));
1025        assert!(cf.contains("meta:GstVideoOverlayComposition"));
1026        assert!(!cf.contains("memory:GLMemory"));
1027    }
1028
1029    #[test]
1030    fn trait_impls_from_id() {
1031        crate::init().unwrap();
1032
1033        let cf = CapsFeatures::from(idstr!("memory:DMABuf"));
1034        assert!(cf.contains_by_id(idstr!("memory:DMABuf")));
1035
1036        let cf = CapsFeatures::from([
1037            idstr!("memory:DMABuf"),
1038            idstr!("meta:GstVideoOverlayComposition"),
1039        ]);
1040
1041        assert!(cf.contains_by_id(idstr!("memory:DMABuf")));
1042        assert!(cf.contains("meta:GstVideoOverlayComposition"));
1043        assert!(!cf.contains("memory:GLMemory"));
1044
1045        let cf = CapsFeatures::from_iter(vec![
1046            idstr!("memory:DMABuf"),
1047            idstr!("meta:GstVideoOverlayComposition"),
1048        ]);
1049
1050        assert!(cf.contains_by_id(idstr!("memory:DMABuf")));
1051        assert!(cf.contains("meta:GstVideoOverlayComposition"));
1052        assert!(!cf.contains("memory:GLMemory"));
1053
1054        let mut cf = CapsFeatures::new_empty();
1055        cf.extend([
1056            idstr!("memory:DMABuf"),
1057            idstr!("meta:GstVideoOverlayComposition"),
1058        ]);
1059
1060        assert!(cf.contains_by_id(idstr!("memory:DMABuf")));
1061        assert!(cf.contains("meta:GstVideoOverlayComposition"));
1062        assert!(!cf.contains("memory:GLMemory"));
1063    }
1064
1065    #[test]
1066    fn trait_impls_from_ref_id() {
1067        crate::init().unwrap();
1068
1069        let dma_buf = idstr!("memory:DMABuf");
1070        let overlay_comp = idstr!("meta:GstVideoOverlayComposition");
1071
1072        let cf = CapsFeatures::from(&dma_buf);
1073        assert!(cf.contains_by_id(&dma_buf));
1074
1075        let cf = CapsFeatures::from([&dma_buf, &overlay_comp]);
1076
1077        assert!(cf.contains_by_id(&dma_buf));
1078        assert!(cf.contains_by_id(&overlay_comp));
1079        assert!(!cf.contains("memory:GLMemory"));
1080
1081        let cf = CapsFeatures::from_iter(vec![&dma_buf, &overlay_comp]);
1082
1083        assert!(cf.contains_by_id(&dma_buf));
1084        assert!(cf.contains_by_id(&overlay_comp));
1085        assert!(!cf.contains("memory:GLMemory"));
1086
1087        let mut cf = CapsFeatures::new_empty();
1088        cf.extend([&dma_buf, &overlay_comp]);
1089
1090        assert!(cf.contains_by_id(dma_buf));
1091        assert!(cf.contains_by_id(overlay_comp));
1092        assert!(!cf.contains("memory:GLMemory"));
1093    }
1094
1095    #[test]
1096    fn test_hash() {
1097        crate::init().unwrap();
1098
1099        use std::hash::BuildHasher;
1100        let bh = std::hash::RandomState::new();
1101
1102        let any = CapsFeatures::new_any();
1103        let empty = CapsFeatures::new_empty();
1104        assert_eq!(bh.hash_one(&any), bh.hash_one(&any));
1105        assert_eq!(bh.hash_one(&empty), bh.hash_one(&empty));
1106        assert_ne!(bh.hash_one(&any), bh.hash_one(&empty));
1107
1108        // Different names
1109        let cf1 = CapsFeatures::from(gstr!("memory:DMABuf"));
1110        let cf2 = CapsFeatures::from(gstr!("memory:GLMemory"));
1111        assert_eq!(bh.hash_one(&cf1), bh.hash_one(&cf1));
1112        assert_eq!(bh.hash_one(&cf2), bh.hash_one(&cf2));
1113        assert_ne!(bh.hash_one(&cf1), bh.hash_one(&cf2));
1114
1115        // Same features, different order
1116        let cf1 = CapsFeatures::from([
1117            gstr!("memory:DMABuf"),
1118            gstr!("meta:GstVideoOverlayComposition"),
1119        ]);
1120        let cf2 = CapsFeatures::from([
1121            gstr!("meta:GstVideoOverlayComposition"),
1122            gstr!("memory:DMABuf"),
1123        ]);
1124        assert_eq!(bh.hash_one(&cf1), bh.hash_one(&cf1));
1125        assert_eq!(bh.hash_one(&cf2), bh.hash_one(&cf2));
1126        assert_eq!(bh.hash_one(&cf1), bh.hash_one(&cf2));
1127    }
1128}