gstreamer_video/
video_info.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{fmt, marker::PhantomData, mem, ptr, str};
4
5use crate::ffi;
6use glib::translate::*;
7use gst::prelude::*;
8
9#[doc(alias = "GST_VIDEO_MAX_PLANES")]
10pub const VIDEO_MAX_PLANES: usize = ffi::GST_VIDEO_MAX_PLANES as usize;
11
12/// Possible color range values. These constants are defined for 8 bit color
13/// values and can be scaled for other bit depths.
14#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
15#[non_exhaustive]
16#[doc(alias = "GstVideoColorRange")]
17pub enum VideoColorRange {
18    /// unknown range
19    #[doc(alias = "GST_VIDEO_COLOR_RANGE_UNKNOWN")]
20    Unknown,
21    #[doc(alias = "GST_VIDEO_COLOR_RANGE_0_255")]
22    Range0_255,
23    #[doc(alias = "GST_VIDEO_COLOR_RANGE_16_235")]
24    Range16_235,
25    #[doc(hidden)]
26    __Unknown(i32),
27}
28
29#[doc(hidden)]
30impl IntoGlib for VideoColorRange {
31    type GlibType = ffi::GstVideoColorRange;
32
33    #[inline]
34    fn into_glib(self) -> ffi::GstVideoColorRange {
35        match self {
36            Self::Unknown => ffi::GST_VIDEO_COLOR_RANGE_UNKNOWN,
37            Self::Range0_255 => ffi::GST_VIDEO_COLOR_RANGE_0_255,
38            Self::Range16_235 => ffi::GST_VIDEO_COLOR_RANGE_16_235,
39            Self::__Unknown(value) => value,
40        }
41    }
42}
43
44#[doc(hidden)]
45impl FromGlib<ffi::GstVideoColorRange> for VideoColorRange {
46    #[inline]
47    unsafe fn from_glib(value: ffi::GstVideoColorRange) -> Self {
48        skip_assert_initialized!();
49        match value {
50            0 => Self::Unknown,
51            1 => Self::Range0_255,
52            2 => Self::Range16_235,
53            value => Self::__Unknown(value),
54        }
55    }
56}
57
58impl StaticType for VideoColorRange {
59    #[inline]
60    fn static_type() -> glib::Type {
61        unsafe { from_glib(ffi::gst_video_color_range_get_type()) }
62    }
63}
64
65impl glib::value::ValueType for VideoColorRange {
66    type Type = Self;
67}
68
69unsafe impl<'a> glib::value::FromValue<'a> for VideoColorRange {
70    type Checker = glib::value::GenericValueTypeChecker<Self>;
71
72    unsafe fn from_value(value: &'a glib::Value) -> Self {
73        skip_assert_initialized!();
74        from_glib(glib::gobject_ffi::g_value_get_enum(value.to_glib_none().0))
75    }
76}
77
78impl ToValue for VideoColorRange {
79    fn to_value(&self) -> glib::Value {
80        let mut value = glib::Value::for_value_type::<Self>();
81        unsafe { glib::gobject_ffi::g_value_set_enum(value.to_glib_none_mut().0, self.into_glib()) }
82        value
83    }
84
85    fn value_type(&self) -> glib::Type {
86        Self::static_type()
87    }
88}
89
90impl From<VideoColorRange> for glib::Value {
91    fn from(v: VideoColorRange) -> glib::Value {
92        skip_assert_initialized!();
93        glib::value::ToValue::to_value(&v)
94    }
95}
96
97/// Structure describing the color info.
98#[doc(alias = "GstVideoColorimetry")]
99#[derive(Copy, Clone)]
100#[repr(transparent)]
101pub struct VideoColorimetry(ffi::GstVideoColorimetry);
102
103impl VideoColorimetry {
104    pub fn new(
105        range: crate::VideoColorRange,
106        matrix: crate::VideoColorMatrix,
107        transfer: crate::VideoTransferFunction,
108        primaries: crate::VideoColorPrimaries,
109    ) -> Self {
110        skip_assert_initialized!();
111
112        let colorimetry = ffi::GstVideoColorimetry {
113            range: range.into_glib(),
114            matrix: matrix.into_glib(),
115            transfer: transfer.into_glib(),
116            primaries: primaries.into_glib(),
117        };
118
119        Self(colorimetry)
120    }
121
122    #[inline]
123    pub fn range(&self) -> crate::VideoColorRange {
124        unsafe { from_glib(self.0.range) }
125    }
126
127    #[inline]
128    pub fn matrix(&self) -> crate::VideoColorMatrix {
129        unsafe { from_glib(self.0.matrix) }
130    }
131
132    #[inline]
133    pub fn transfer(&self) -> crate::VideoTransferFunction {
134        unsafe { from_glib(self.0.transfer) }
135    }
136
137    #[inline]
138    pub fn primaries(&self) -> crate::VideoColorPrimaries {
139        unsafe { from_glib(self.0.primaries) }
140    }
141
142    /// Compare the 2 colorimetry sets for functionally equality
143    /// ## `bitdepth`
144    /// bitdepth of a format associated with `self`
145    /// ## `other`
146    /// another [`VideoColorimetry`][crate::VideoColorimetry]
147    /// ## `other_bitdepth`
148    /// bitdepth of a format associated with `other`
149    ///
150    /// # Returns
151    ///
152    /// [`true`] if `self` and `other` are equivalent.
153    #[cfg(feature = "v1_22")]
154    #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
155    #[doc(alias = "gst_video_colorimetry_is_equivalent")]
156    pub fn is_equivalent(&self, bitdepth: u32, other: &Self, other_bitdepth: u32) -> bool {
157        unsafe {
158            from_glib(ffi::gst_video_colorimetry_is_equivalent(
159                &self.0,
160                bitdepth,
161                &other.0,
162                other_bitdepth,
163            ))
164        }
165    }
166}
167
168impl PartialEq for VideoColorimetry {
169    #[doc(alias = "gst_video_colorimetry_is_equal")]
170    fn eq(&self, other: &Self) -> bool {
171        unsafe { from_glib(ffi::gst_video_colorimetry_is_equal(&self.0, &other.0)) }
172    }
173}
174
175impl Eq for VideoColorimetry {}
176
177impl str::FromStr for crate::VideoColorimetry {
178    type Err = glib::error::BoolError;
179
180    #[doc(alias = "gst_video_colorimetry_from_string")]
181    fn from_str(s: &str) -> Result<Self, Self::Err> {
182        assert_initialized_main_thread!();
183
184        unsafe {
185            let mut colorimetry = mem::MaybeUninit::uninit();
186            let valid: bool = from_glib(ffi::gst_video_colorimetry_from_string(
187                colorimetry.as_mut_ptr(),
188                s.to_glib_none().0,
189            ));
190            if valid {
191                Ok(Self(colorimetry.assume_init()))
192            } else {
193                Err(glib::bool_error!("Invalid colorimetry info"))
194            }
195        }
196    }
197}
198
199impl fmt::Debug for crate::VideoColorimetry {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        f.debug_struct("VideoColorimetry")
202            .field("range", &self.0.range)
203            .field("matrix", &self.0.matrix)
204            .field("transfer", &self.0.transfer)
205            .field("primaries", &self.0.primaries)
206            .finish()
207    }
208}
209
210impl fmt::Display for crate::VideoColorimetry {
211    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        let s =
213            unsafe { glib::GString::from_glib_full(ffi::gst_video_colorimetry_to_string(&self.0)) };
214        f.write_str(&s)
215    }
216}
217
218impl crate::VideoChromaSite {
219    #[doc(alias = "gst_video_chroma_site_to_string")]
220    #[doc(alias = "gst_video_chroma_to_string")]
221    pub fn to_str(self) -> glib::GString {
222        assert_initialized_main_thread!();
223
224        unsafe {
225            cfg_if::cfg_if! {
226                if #[cfg(feature = "v1_20")] {
227                    from_glib_full(ffi::gst_video_chroma_site_to_string(self.into_glib()))
228                } else {
229                    from_glib_none(ffi::gst_video_chroma_to_string(self.into_glib()))
230                }
231            }
232        }
233    }
234}
235
236impl str::FromStr for crate::VideoChromaSite {
237    type Err = glib::error::BoolError;
238
239    #[doc(alias = "gst_video_chroma_from_string")]
240    fn from_str(s: &str) -> Result<Self, Self::Err> {
241        skip_assert_initialized!();
242
243        cfg_if::cfg_if! {
244            if #[cfg(feature = "v1_20")] {
245                let chroma_site = Self::from_string(s);
246            } else {
247                assert_initialized_main_thread!();
248                let chroma_site: Self =
249                    unsafe { from_glib(ffi::gst_video_chroma_from_string(s.to_glib_none().0)) };
250            }
251        };
252
253        if chroma_site.is_empty() {
254            Err(glib::bool_error!("Invalid chroma site"))
255        } else {
256            Ok(chroma_site)
257        }
258    }
259}
260
261impl From<crate::VideoMultiviewFramePacking> for crate::VideoMultiviewMode {
262    #[inline]
263    fn from(v: crate::VideoMultiviewFramePacking) -> Self {
264        skip_assert_initialized!();
265        unsafe { from_glib(v.into_glib()) }
266    }
267}
268
269impl TryFrom<crate::VideoMultiviewMode> for crate::VideoMultiviewFramePacking {
270    type Error = glib::BoolError;
271
272    fn try_from(v: crate::VideoMultiviewMode) -> Result<Self, glib::BoolError> {
273        skip_assert_initialized!();
274
275        let v2 = unsafe { from_glib(v.into_glib()) };
276
277        if let Self::__Unknown(_) = v2 {
278            Err(glib::bool_error!("Invalid frame packing mode"))
279        } else {
280            Ok(v2)
281        }
282    }
283}
284
285/// Information describing image properties. This information can be filled
286/// in from GstCaps with [`from_caps()`][Self::from_caps()]. The information is also used
287/// to store the specific video info when mapping a video frame with
288/// [`VideoFrame::from_buffer_readable()`][crate::VideoFrame::from_buffer_readable()].
289///
290/// Use the provided macros to access the info in this structure.
291#[doc(alias = "GstVideoInfo")]
292#[derive(Clone)]
293#[repr(transparent)]
294pub struct VideoInfo(pub(crate) ffi::GstVideoInfo);
295
296impl fmt::Debug for VideoInfo {
297    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
298        f.debug_struct("VideoInfo")
299            .field("format", &self.format())
300            .field("format-info", &self.format_info())
301            .field("width", &self.width())
302            .field("height", &self.height())
303            .field("interlace_mode", &self.interlace_mode())
304            .field("flags", &self.flags())
305            .field("size", &self.size())
306            .field("views", &self.views())
307            .field("chroma_site", &self.chroma_site())
308            .field("colorimetry", &self.colorimetry())
309            .field("par", &self.par())
310            .field("fps", &self.fps())
311            .field("offset", &self.offset())
312            .field("stride", &self.stride())
313            .field("multiview_mode", &self.multiview_mode())
314            .field("multiview_flags", &self.multiview_flags())
315            .field("field_order", &self.field_order())
316            .finish()
317    }
318}
319
320#[derive(Debug)]
321#[must_use = "The builder must be built to be used"]
322pub struct VideoInfoBuilder<'a> {
323    format: crate::VideoFormat,
324    width: u32,
325    height: u32,
326    interlace_mode: Option<crate::VideoInterlaceMode>,
327    flags: Option<crate::VideoFlags>,
328    size: Option<usize>,
329    views: Option<u32>,
330    chroma_site: Option<crate::VideoChromaSite>,
331    colorimetry: Option<&'a crate::VideoColorimetry>,
332    par: Option<gst::Fraction>,
333    fps: Option<gst::Fraction>,
334    offset: Option<&'a [usize]>,
335    stride: Option<&'a [i32]>,
336    multiview_mode: Option<crate::VideoMultiviewMode>,
337    multiview_flags: Option<crate::VideoMultiviewFlags>,
338    field_order: Option<crate::VideoFieldOrder>,
339}
340
341impl<'a> VideoInfoBuilder<'a> {
342    pub fn build(self) -> Result<VideoInfo, glib::error::BoolError> {
343        unsafe {
344            let mut info = mem::MaybeUninit::uninit();
345
346            cfg_if::cfg_if! {
347                if #[cfg(feature = "v1_16")] {
348                    let res: bool = {
349                        from_glib(if let Some(interlace_mode) = self.interlace_mode {
350                            ffi::gst_video_info_set_interlaced_format(
351                                info.as_mut_ptr(),
352                                self.format.into_glib(),
353                                interlace_mode.into_glib(),
354                                self.width,
355                                self.height,
356                            )
357                        } else {
358                            ffi::gst_video_info_set_format(
359                                info.as_mut_ptr(),
360                                self.format.into_glib(),
361                                self.width,
362                                self.height,
363                            )
364                        })
365                    };
366                } else {
367                    let res: bool = {
368                        let res = from_glib(ffi::gst_video_info_set_format(
369                            info.as_mut_ptr(),
370                            self.format.into_glib(),
371                            self.width,
372                            self.height,
373                        ));
374
375                        if res {
376                            if let Some(interlace_mode) = self.interlace_mode {
377                                let info = info.as_mut_ptr();
378                                (*info).interlace_mode = interlace_mode.into_glib();
379                            }
380                        }
381
382                        res
383                    };
384                }
385            }
386
387            if !res {
388                return Err(glib::bool_error!("Failed to build VideoInfo"));
389            }
390
391            let mut info = info.assume_init();
392
393            if info.finfo.is_null() || info.width <= 0 || info.height <= 0 {
394                return Err(glib::bool_error!("Failed to build VideoInfo"));
395            }
396
397            if let Some(flags) = self.flags {
398                info.flags = flags.into_glib();
399            }
400
401            if let Some(size) = self.size {
402                info.size = size;
403            }
404
405            if let Some(views) = self.views {
406                info.views = views as i32;
407            }
408
409            if let Some(chroma_site) = self.chroma_site {
410                info.chroma_site = chroma_site.into_glib();
411            }
412
413            if let Some(colorimetry) = self.colorimetry {
414                ptr::write(&mut info.colorimetry, ptr::read(&colorimetry.0));
415            }
416
417            if let Some(par) = self.par {
418                info.par_n = par.numer();
419                info.par_d = par.denom();
420            }
421
422            if let Some(fps) = self.fps {
423                info.fps_n = fps.numer();
424                info.fps_d = fps.denom();
425            }
426
427            if let Some(offset) = self.offset {
428                if offset.len() != ((*info.finfo).n_planes as usize) {
429                    return Err(glib::bool_error!("Failed to build VideoInfo"));
430                }
431
432                let n_planes = (*info.finfo).n_planes as usize;
433                info.offset[..n_planes].copy_from_slice(&offset[..n_planes]);
434            }
435
436            if let Some(stride) = self.stride {
437                if stride.len() != ((*info.finfo).n_planes as usize) {
438                    return Err(glib::bool_error!("Failed to build VideoInfo"));
439                }
440
441                let n_planes = (*info.finfo).n_planes as usize;
442                info.stride[..n_planes].copy_from_slice(&stride[..n_planes]);
443            }
444
445            if let Some(multiview_mode) = self.multiview_mode {
446                let ptr = &mut info.ABI._gst_reserved as *mut _ as *mut i32;
447                ptr::write(ptr.offset(0), multiview_mode.into_glib());
448            }
449
450            if let Some(multiview_flags) = self.multiview_flags {
451                let ptr = &mut info.ABI._gst_reserved as *mut _ as *mut u32;
452                ptr::write(ptr.offset(1), multiview_flags.into_glib());
453            }
454
455            if let Some(field_order) = self.field_order {
456                let ptr = &mut info.ABI._gst_reserved as *mut _ as *mut i32;
457                ptr::write(ptr.offset(2), field_order.into_glib());
458            }
459
460            Ok(VideoInfo(info))
461        }
462    }
463
464    pub fn interlace_mode(self, interlace_mode: crate::VideoInterlaceMode) -> VideoInfoBuilder<'a> {
465        Self {
466            interlace_mode: Some(interlace_mode),
467            ..self
468        }
469    }
470
471    pub fn interlace_mode_if(
472        self,
473        interlace_mode: crate::VideoInterlaceMode,
474        predicate: bool,
475    ) -> VideoInfoBuilder<'a> {
476        if predicate {
477            self.interlace_mode(interlace_mode)
478        } else {
479            self
480        }
481    }
482
483    pub fn interlace_mode_if_some(
484        self,
485        interlace_mode: Option<crate::VideoInterlaceMode>,
486    ) -> VideoInfoBuilder<'a> {
487        if let Some(interlace_mode) = interlace_mode {
488            self.interlace_mode(interlace_mode)
489        } else {
490            self
491        }
492    }
493
494    pub fn flags(self, flags: crate::VideoFlags) -> Self {
495        Self {
496            flags: Some(flags),
497            ..self
498        }
499    }
500
501    pub fn flags_if(self, flags: crate::VideoFlags, predicate: bool) -> Self {
502        if predicate {
503            self.flags(flags)
504        } else {
505            self
506        }
507    }
508
509    pub fn flags_if_some(self, flags: Option<crate::VideoFlags>) -> Self {
510        if let Some(flags) = flags {
511            self.flags(flags)
512        } else {
513            self
514        }
515    }
516
517    pub fn size(self, size: usize) -> Self {
518        Self {
519            size: Some(size),
520            ..self
521        }
522    }
523
524    pub fn size_if(self, size: usize, predicate: bool) -> Self {
525        if predicate {
526            self.size(size)
527        } else {
528            self
529        }
530    }
531
532    pub fn size_if_some(self, size: Option<usize>) -> Self {
533        if let Some(size) = size {
534            self.size(size)
535        } else {
536            self
537        }
538    }
539
540    pub fn views(self, views: u32) -> Self {
541        Self {
542            views: Some(views),
543            ..self
544        }
545    }
546
547    pub fn views_if(self, views: u32, predicate: bool) -> Self {
548        if predicate {
549            self.views(views)
550        } else {
551            self
552        }
553    }
554
555    pub fn views_if_some(self, views: Option<u32>) -> Self {
556        if let Some(views) = views {
557            self.views(views)
558        } else {
559            self
560        }
561    }
562
563    pub fn chroma_site(self, chroma_site: crate::VideoChromaSite) -> Self {
564        Self {
565            chroma_site: Some(chroma_site),
566            ..self
567        }
568    }
569
570    pub fn chroma_site_if(self, chroma_site: crate::VideoChromaSite, predicate: bool) -> Self {
571        if predicate {
572            self.chroma_site(chroma_site)
573        } else {
574            self
575        }
576    }
577
578    pub fn chroma_site_if_some(self, chroma_site: Option<crate::VideoChromaSite>) -> Self {
579        if let Some(chroma_site) = chroma_site {
580            self.chroma_site(chroma_site)
581        } else {
582            self
583        }
584    }
585
586    pub fn colorimetry(self, colorimetry: &'a crate::VideoColorimetry) -> VideoInfoBuilder<'a> {
587        Self {
588            colorimetry: Some(colorimetry),
589            ..self
590        }
591    }
592
593    pub fn colorimetry_if(
594        self,
595        colorimetry: &'a crate::VideoColorimetry,
596        predicate: bool,
597    ) -> VideoInfoBuilder<'a> {
598        if predicate {
599            self.colorimetry(colorimetry)
600        } else {
601            self
602        }
603    }
604
605    pub fn colorimetry_if_some(
606        self,
607        colorimetry: Option<&'a crate::VideoColorimetry>,
608    ) -> VideoInfoBuilder<'a> {
609        if let Some(colorimetry) = colorimetry {
610            self.colorimetry(colorimetry)
611        } else {
612            self
613        }
614    }
615
616    pub fn par<T: Into<gst::Fraction>>(self, par: T) -> Self {
617        Self {
618            par: Some(par.into()),
619            ..self
620        }
621    }
622
623    pub fn par_if<T: Into<gst::Fraction>>(self, par: T, predicate: bool) -> Self {
624        if predicate {
625            self.par(par)
626        } else {
627            self
628        }
629    }
630
631    pub fn par_if_some<T: Into<gst::Fraction>>(self, par: Option<T>) -> Self {
632        if let Some(par) = par {
633            self.par(par)
634        } else {
635            self
636        }
637    }
638
639    pub fn fps<T: Into<gst::Fraction>>(self, fps: T) -> Self {
640        Self {
641            fps: Some(fps.into()),
642            ..self
643        }
644    }
645
646    pub fn fps_if<T: Into<gst::Fraction>>(self, fps: T, predicate: bool) -> Self {
647        if predicate {
648            self.fps(fps)
649        } else {
650            self
651        }
652    }
653
654    pub fn fps_if_some<T: Into<gst::Fraction>>(self, fps: Option<T>) -> Self {
655        if let Some(fps) = fps {
656            self.fps(fps)
657        } else {
658            self
659        }
660    }
661
662    pub fn offset(self, offset: &'a [usize]) -> VideoInfoBuilder<'a> {
663        Self {
664            offset: Some(offset),
665            ..self
666        }
667    }
668
669    pub fn offset_if(self, offset: &'a [usize], predicate: bool) -> VideoInfoBuilder<'a> {
670        if predicate {
671            self.offset(offset)
672        } else {
673            self
674        }
675    }
676
677    pub fn offset_if_some(self, offset: Option<&'a [usize]>) -> VideoInfoBuilder<'a> {
678        if let Some(offset) = offset {
679            self.offset(offset)
680        } else {
681            self
682        }
683    }
684
685    pub fn stride(self, stride: &'a [i32]) -> VideoInfoBuilder<'a> {
686        Self {
687            stride: Some(stride),
688            ..self
689        }
690    }
691
692    pub fn stride_if(self, stride: &'a [i32], predicate: bool) -> VideoInfoBuilder<'a> {
693        if predicate {
694            self.stride(stride)
695        } else {
696            self
697        }
698    }
699
700    pub fn stride_if_some(self, stride: Option<&'a [i32]>) -> VideoInfoBuilder<'a> {
701        if let Some(stride) = stride {
702            self.stride(stride)
703        } else {
704            self
705        }
706    }
707
708    pub fn multiview_mode(self, multiview_mode: crate::VideoMultiviewMode) -> Self {
709        Self {
710            multiview_mode: Some(multiview_mode),
711            ..self
712        }
713    }
714
715    pub fn multiview_mode_if(
716        self,
717        multiview_mode: crate::VideoMultiviewMode,
718        predicate: bool,
719    ) -> Self {
720        if predicate {
721            self.multiview_mode(multiview_mode)
722        } else {
723            self
724        }
725    }
726
727    pub fn multiview_mode_if_some(self, multiview_mode: Option<crate::VideoMultiviewMode>) -> Self {
728        if let Some(multiview_mode) = multiview_mode {
729            self.multiview_mode(multiview_mode)
730        } else {
731            self
732        }
733    }
734
735    pub fn multiview_flags(self, multiview_flags: crate::VideoMultiviewFlags) -> Self {
736        Self {
737            multiview_flags: Some(multiview_flags),
738            ..self
739        }
740    }
741
742    pub fn multiview_flags_if(
743        self,
744        multiview_flags: crate::VideoMultiviewFlags,
745        predicate: bool,
746    ) -> Self {
747        if predicate {
748            self.multiview_flags(multiview_flags)
749        } else {
750            self
751        }
752    }
753
754    pub fn multiview_flags_if_some(
755        self,
756        multiview_flags: Option<crate::VideoMultiviewFlags>,
757    ) -> Self {
758        if let Some(multiview_flags) = multiview_flags {
759            self.multiview_flags(multiview_flags)
760        } else {
761            self
762        }
763    }
764
765    pub fn field_order(self, field_order: crate::VideoFieldOrder) -> Self {
766        Self {
767            field_order: Some(field_order),
768            ..self
769        }
770    }
771
772    pub fn field_order_if(self, field_order: crate::VideoFieldOrder, predicate: bool) -> Self {
773        if predicate {
774            self.field_order(field_order)
775        } else {
776            self
777        }
778    }
779
780    pub fn field_order_if_some(self, field_order: Option<crate::VideoFieldOrder>) -> Self {
781        if let Some(field_order) = field_order {
782            self.field_order(field_order)
783        } else {
784            self
785        }
786    }
787}
788
789impl VideoInfo {
790    pub fn builder<'a>(
791        format: crate::VideoFormat,
792        width: u32,
793        height: u32,
794    ) -> VideoInfoBuilder<'a> {
795        assert_initialized_main_thread!();
796
797        VideoInfoBuilder {
798            format,
799            width,
800            height,
801            interlace_mode: None,
802            flags: None,
803            size: None,
804            views: None,
805            chroma_site: None,
806            colorimetry: None,
807            par: None,
808            fps: None,
809            offset: None,
810            stride: None,
811            multiview_mode: None,
812            multiview_flags: None,
813            field_order: None,
814        }
815    }
816
817    #[inline]
818    pub fn is_valid(&self) -> bool {
819        !self.0.finfo.is_null() && self.0.width > 0 && self.0.height > 0 && self.0.size > 0
820    }
821
822    /// Parse `caps` to generate a [`VideoInfo`][crate::VideoInfo].
823    /// ## `caps`
824    /// a [`gst::Caps`][crate::gst::Caps]
825    ///
826    /// # Returns
827    ///
828    /// A [`VideoInfo`][crate::VideoInfo], or [`None`] if `caps` couldn't be parsed
829    #[doc(alias = "gst_video_info_from_caps")]
830    pub fn from_caps(caps: &gst::CapsRef) -> Result<Self, glib::error::BoolError> {
831        skip_assert_initialized!();
832
833        unsafe {
834            let mut info = mem::MaybeUninit::uninit();
835            if from_glib(ffi::gst_video_info_from_caps(
836                info.as_mut_ptr(),
837                caps.as_ptr(),
838            )) {
839                Ok(Self(info.assume_init()))
840            } else {
841                Err(glib::bool_error!("Failed to create VideoInfo from caps"))
842            }
843        }
844    }
845
846    /// Convert the values of `self` into a [`gst::Caps`][crate::gst::Caps].
847    ///
848    /// # Returns
849    ///
850    /// a new [`gst::Caps`][crate::gst::Caps] containing the info of `self`.
851    #[doc(alias = "gst_video_info_to_caps")]
852    pub fn to_caps(&self) -> Result<gst::Caps, glib::error::BoolError> {
853        unsafe {
854            let result = from_glib_full(ffi::gst_video_info_to_caps(mut_override(&self.0)));
855            match result {
856                Some(c) => Ok(c),
857                None => Err(glib::bool_error!("Failed to create caps from VideoInfo")),
858            }
859        }
860    }
861
862    #[inline]
863    pub fn format(&self) -> crate::VideoFormat {
864        if self.0.finfo.is_null() {
865            return crate::VideoFormat::Unknown;
866        }
867
868        self.format_info().format()
869    }
870
871    #[inline]
872    pub fn format_info(&self) -> crate::VideoFormatInfo {
873        unsafe { crate::VideoFormatInfo::from_ptr(self.0.finfo) }
874    }
875
876    #[inline]
877    pub fn name<'a>(&self) -> &'a str {
878        self.format_info().name()
879    }
880
881    #[inline]
882    pub fn width(&self) -> u32 {
883        self.0.width as u32
884    }
885
886    #[inline]
887    pub fn height(&self) -> u32 {
888        self.0.height as u32
889    }
890
891    #[cfg(feature = "v1_16")]
892    #[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
893    #[inline]
894    pub fn field_height(&self) -> u32 {
895        if self.0.interlace_mode == ffi::GST_VIDEO_INTERLACE_MODE_ALTERNATE {
896            (self.0.height as u32 + 1) / 2
897        } else {
898            self.0.height as u32
899        }
900    }
901
902    #[inline]
903    pub fn interlace_mode(&self) -> crate::VideoInterlaceMode {
904        unsafe { from_glib(self.0.interlace_mode) }
905    }
906
907    #[inline]
908    pub fn flags(&self) -> crate::VideoFlags {
909        unsafe { from_glib(self.0.flags) }
910    }
911
912    #[inline]
913    pub fn size(&self) -> usize {
914        self.0.size
915    }
916
917    #[inline]
918    pub fn views(&self) -> u32 {
919        self.0.views as u32
920    }
921
922    #[inline]
923    pub fn chroma_site(&self) -> crate::VideoChromaSite {
924        unsafe { from_glib(self.0.chroma_site) }
925    }
926
927    #[inline]
928    pub fn colorimetry(&self) -> VideoColorimetry {
929        unsafe { VideoColorimetry(ptr::read(&self.0.colorimetry)) }
930    }
931
932    #[inline]
933    pub fn comp_depth(&self, component: u8) -> u32 {
934        self.format_info().depth()[component as usize]
935    }
936
937    #[inline]
938    pub fn comp_height(&self, component: u8) -> u32 {
939        self.format_info().scale_height(component, self.height())
940    }
941
942    #[inline]
943    pub fn comp_width(&self, component: u8) -> u32 {
944        self.format_info().scale_width(component, self.width())
945    }
946
947    #[inline]
948    pub fn comp_offset(&self, component: u8) -> usize {
949        self.offset()[self.format_info().plane()[component as usize] as usize]
950            + self.format_info().poffset()[component as usize] as usize
951    }
952
953    #[inline]
954    pub fn comp_plane(&self, component: u8) -> u32 {
955        self.format_info().plane()[component as usize]
956    }
957
958    #[inline]
959    pub fn comp_poffset(&self, component: u8) -> u32 {
960        self.format_info().poffset()[component as usize]
961    }
962
963    #[inline]
964    pub fn comp_pstride(&self, component: u8) -> i32 {
965        self.format_info().pixel_stride()[component as usize]
966    }
967
968    #[inline]
969    pub fn comp_stride(&self, component: u8) -> i32 {
970        self.stride()[self.format_info().plane()[component as usize] as usize]
971    }
972
973    #[inline]
974    pub fn par(&self) -> gst::Fraction {
975        gst::Fraction::new(self.0.par_n, self.0.par_d)
976    }
977
978    #[inline]
979    pub fn fps(&self) -> gst::Fraction {
980        gst::Fraction::new(self.0.fps_n, self.0.fps_d)
981    }
982
983    #[cfg(feature = "v1_16")]
984    #[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
985    #[inline]
986    pub fn field_rate(&self) -> gst::Fraction {
987        if self.interlace_mode() == crate::VideoInterlaceMode::Alternate {
988            2 * self.fps()
989        } else {
990            self.fps()
991        }
992    }
993
994    #[inline]
995    pub fn offset(&self) -> &[usize] {
996        &self.0.offset[0..(self.format_info().n_planes() as usize)]
997    }
998
999    #[inline]
1000    pub fn stride(&self) -> &[i32] {
1001        &self.0.stride[0..(self.format_info().n_planes() as usize)]
1002    }
1003
1004    #[inline]
1005    pub fn multiview_mode(&self) -> crate::VideoMultiviewMode {
1006        unsafe {
1007            let ptr = &self.0.ABI._gst_reserved as *const _ as *const i32;
1008            from_glib(ptr::read(ptr.offset(0)))
1009        }
1010    }
1011
1012    #[inline]
1013    pub fn multiview_flags(&self) -> crate::VideoMultiviewFlags {
1014        unsafe {
1015            let ptr = &self.0.ABI._gst_reserved as *const _ as *const u32;
1016            from_glib(ptr::read(ptr.offset(1)))
1017        }
1018    }
1019
1020    #[inline]
1021    pub fn field_order(&self) -> crate::VideoFieldOrder {
1022        unsafe {
1023            let ptr = &self.0.ABI._gst_reserved as *const _ as *const i32;
1024            from_glib(ptr::read(ptr.offset(2)))
1025        }
1026    }
1027
1028    #[inline]
1029    pub fn has_alpha(&self) -> bool {
1030        self.format_info().has_alpha()
1031    }
1032
1033    #[inline]
1034    pub fn is_gray(&self) -> bool {
1035        self.format_info().is_gray()
1036    }
1037
1038    #[inline]
1039    pub fn is_rgb(&self) -> bool {
1040        self.format_info().is_rgb()
1041    }
1042
1043    #[inline]
1044    pub fn is_yuv(&self) -> bool {
1045        self.format_info().is_yuv()
1046    }
1047
1048    #[inline]
1049    pub fn is_interlaced(&self) -> bool {
1050        self.interlace_mode() != crate::VideoInterlaceMode::Progressive
1051    }
1052
1053    #[inline]
1054    pub fn n_planes(&self) -> u32 {
1055        self.format_info().n_planes()
1056    }
1057
1058    #[inline]
1059    pub fn n_components(&self) -> u32 {
1060        self.format_info().n_components()
1061    }
1062
1063    /// Converts among various [`gst::Format`][crate::gst::Format] types. This function handles
1064    /// GST_FORMAT_BYTES, GST_FORMAT_TIME, and GST_FORMAT_DEFAULT. For
1065    /// raw video, GST_FORMAT_DEFAULT corresponds to video frames. This
1066    /// function can be used to handle pad queries of the type GST_QUERY_CONVERT.
1067    /// ## `src_format`
1068    /// [`gst::Format`][crate::gst::Format] of the `src_value`
1069    /// ## `src_value`
1070    /// value to convert
1071    /// ## `dest_format`
1072    /// [`gst::Format`][crate::gst::Format] of the `dest_value`
1073    ///
1074    /// # Returns
1075    ///
1076    /// TRUE if the conversion was successful.
1077    ///
1078    /// ## `dest_value`
1079    /// pointer to destination value
1080    #[doc(alias = "gst_video_info_convert")]
1081    pub fn convert<U: gst::format::SpecificFormattedValueFullRange>(
1082        &self,
1083        src_val: impl gst::format::FormattedValue,
1084    ) -> Option<U> {
1085        skip_assert_initialized!();
1086        unsafe {
1087            let mut dest_val = mem::MaybeUninit::uninit();
1088            if from_glib(ffi::gst_video_info_convert(
1089                mut_override(&self.0),
1090                src_val.format().into_glib(),
1091                src_val.into_raw_value(),
1092                U::default_format().into_glib(),
1093                dest_val.as_mut_ptr(),
1094            )) {
1095                Some(U::from_raw(U::default_format(), dest_val.assume_init()))
1096            } else {
1097                None
1098            }
1099        }
1100    }
1101
1102    pub fn convert_generic(
1103        &self,
1104        src_val: impl gst::format::FormattedValue,
1105        dest_fmt: gst::Format,
1106    ) -> Option<gst::GenericFormattedValue> {
1107        skip_assert_initialized!();
1108        unsafe {
1109            let mut dest_val = mem::MaybeUninit::uninit();
1110            if from_glib(ffi::gst_video_info_convert(
1111                mut_override(&self.0),
1112                src_val.format().into_glib(),
1113                src_val.into_raw_value(),
1114                dest_fmt.into_glib(),
1115                dest_val.as_mut_ptr(),
1116            )) {
1117                Some(gst::GenericFormattedValue::new(
1118                    dest_fmt,
1119                    dest_val.assume_init(),
1120                ))
1121            } else {
1122                None
1123            }
1124        }
1125    }
1126
1127    /// Adjust the offset and stride fields in `self` so that the padding and
1128    /// stride alignment in `align` is respected.
1129    ///
1130    /// Extra padding will be added to the right side when stride alignment padding
1131    /// is required and `align` will be updated with the new padding values.
1132    /// ## `align`
1133    /// alignment parameters
1134    ///
1135    /// # Returns
1136    ///
1137    /// [`false`] if alignment could not be applied, e.g. because the
1138    ///  size of a frame can't be represented as a 32 bit integer (Since: 1.12)
1139    #[doc(alias = "gst_video_info_align")]
1140    pub fn align(&mut self, align: &mut crate::VideoAlignment) -> Result<(), glib::BoolError> {
1141        unsafe {
1142            glib::result_from_gboolean!(
1143                ffi::gst_video_info_align(&mut self.0, &mut align.0,),
1144                "Failed to align VideoInfo"
1145            )
1146        }
1147    }
1148
1149    /// Extra padding will be added to the right side when stride alignment padding
1150    /// is required and `align` will be updated with the new padding values.
1151    ///
1152    /// This variant of [`align()`][Self::align()] provides the updated size, in bytes,
1153    /// of each video plane after the alignment, including all horizontal and vertical
1154    /// paddings.
1155    ///
1156    /// In case of GST_VIDEO_INTERLACE_MODE_ALTERNATE info, the returned sizes are the
1157    /// ones used to hold a single field, not the full frame.
1158    /// ## `align`
1159    /// alignment parameters
1160    ///
1161    /// # Returns
1162    ///
1163    /// [`false`] if alignment could not be applied, e.g. because the
1164    ///  size of a frame can't be represented as a 32 bit integer
1165    ///
1166    /// ## `plane_size`
1167    /// array used to store the plane sizes
1168    #[cfg(feature = "v1_18")]
1169    #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
1170    #[doc(alias = "gst_video_info_align_full")]
1171    pub fn align_full(
1172        &mut self,
1173        align: &mut crate::VideoAlignment,
1174    ) -> Result<[usize; crate::VIDEO_MAX_PLANES], glib::BoolError> {
1175        let mut plane_size = [0; crate::VIDEO_MAX_PLANES];
1176
1177        unsafe {
1178            glib::result_from_gboolean!(
1179                ffi::gst_video_info_align_full(&mut self.0, &mut align.0, plane_size.as_mut_ptr()),
1180                "Failed to align VideoInfo"
1181            )?;
1182        }
1183
1184        Ok(plane_size)
1185    }
1186
1187    #[doc(alias = "gst_video_color_range_offsets")]
1188    #[inline]
1189    pub fn range_offsets(&self, range: crate::VideoColorRange) -> ([i32; 4], [i32; 4]) {
1190        self.format_info().range_offsets(range)
1191    }
1192}
1193
1194impl PartialEq for VideoInfo {
1195    #[doc(alias = "gst_video_info_is_equal")]
1196    fn eq(&self, other: &Self) -> bool {
1197        unsafe { from_glib(ffi::gst_video_info_is_equal(&self.0, &other.0)) }
1198    }
1199}
1200
1201impl Eq for VideoInfo {}
1202
1203unsafe impl Send for VideoInfo {}
1204unsafe impl Sync for VideoInfo {}
1205
1206impl glib::types::StaticType for VideoInfo {
1207    #[inline]
1208    fn static_type() -> glib::types::Type {
1209        unsafe { glib::translate::from_glib(ffi::gst_video_info_get_type()) }
1210    }
1211}
1212
1213impl glib::value::ValueType for VideoInfo {
1214    type Type = Self;
1215}
1216
1217#[doc(hidden)]
1218unsafe impl<'a> glib::value::FromValue<'a> for VideoInfo {
1219    type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
1220
1221    unsafe fn from_value(value: &'a glib::Value) -> Self {
1222        skip_assert_initialized!();
1223        from_glib_none(
1224            glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut ffi::GstVideoInfo
1225        )
1226    }
1227}
1228
1229#[doc(hidden)]
1230impl glib::value::ToValue for VideoInfo {
1231    fn to_value(&self) -> glib::Value {
1232        let mut value = glib::Value::for_value_type::<Self>();
1233        unsafe {
1234            glib::gobject_ffi::g_value_set_boxed(
1235                value.to_glib_none_mut().0,
1236                self.to_glib_none().0 as *mut _,
1237            )
1238        }
1239        value
1240    }
1241
1242    fn value_type(&self) -> glib::Type {
1243        Self::static_type()
1244    }
1245}
1246
1247#[doc(hidden)]
1248impl glib::value::ToValueOptional for VideoInfo {
1249    fn to_value_optional(s: Option<&Self>) -> glib::Value {
1250        skip_assert_initialized!();
1251        let mut value = glib::Value::for_value_type::<Self>();
1252        unsafe {
1253            glib::gobject_ffi::g_value_set_boxed(
1254                value.to_glib_none_mut().0,
1255                s.to_glib_none().0 as *mut _,
1256            )
1257        }
1258        value
1259    }
1260}
1261
1262#[doc(hidden)]
1263impl From<VideoInfo> for glib::Value {
1264    fn from(v: VideoInfo) -> glib::Value {
1265        skip_assert_initialized!();
1266        glib::value::ToValue::to_value(&v)
1267    }
1268}
1269
1270#[doc(hidden)]
1271impl glib::translate::Uninitialized for VideoInfo {
1272    #[inline]
1273    unsafe fn uninitialized() -> Self {
1274        mem::zeroed()
1275    }
1276}
1277
1278#[doc(hidden)]
1279impl glib::translate::GlibPtrDefault for VideoInfo {
1280    type GlibType = *mut ffi::GstVideoInfo;
1281}
1282
1283#[doc(hidden)]
1284impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstVideoInfo> for VideoInfo {
1285    type Storage = PhantomData<&'a Self>;
1286
1287    #[inline]
1288    fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstVideoInfo, Self> {
1289        glib::translate::Stash(&self.0, PhantomData)
1290    }
1291
1292    fn to_glib_full(&self) -> *const ffi::GstVideoInfo {
1293        unimplemented!()
1294    }
1295}
1296
1297#[doc(hidden)]
1298impl glib::translate::FromGlibPtrNone<*const ffi::GstVideoInfo> for VideoInfo {
1299    #[inline]
1300    unsafe fn from_glib_none(ptr: *const ffi::GstVideoInfo) -> Self {
1301        Self(ptr::read(ptr))
1302    }
1303}
1304
1305#[doc(hidden)]
1306impl glib::translate::FromGlibPtrNone<*mut ffi::GstVideoInfo> for VideoInfo {
1307    #[inline]
1308    unsafe fn from_glib_none(ptr: *mut ffi::GstVideoInfo) -> Self {
1309        Self(ptr::read(ptr))
1310    }
1311}
1312
1313#[doc(hidden)]
1314impl glib::translate::FromGlibPtrFull<*mut ffi::GstVideoInfo> for VideoInfo {
1315    #[inline]
1316    unsafe fn from_glib_full(ptr: *mut ffi::GstVideoInfo) -> Self {
1317        let info = from_glib_none(ptr);
1318        glib::ffi::g_free(ptr as *mut _);
1319        info
1320    }
1321}
1322
1323impl crate::VideoFieldOrder {
1324    #[doc(alias = "gst_video_field_order_to_string")]
1325    pub fn to_str<'a>(self) -> &'a str {
1326        use std::ffi::CStr;
1327
1328        if self == Self::Unknown {
1329            return "UNKNOWN";
1330        }
1331        unsafe {
1332            CStr::from_ptr(
1333                ffi::gst_video_field_order_to_string(self.into_glib())
1334                    .as_ref()
1335                    .expect("gst_video_field_order_to_string returned NULL"),
1336            )
1337            .to_str()
1338            .expect("gst_video_field_order_to_string returned an invalid string")
1339        }
1340    }
1341}
1342
1343impl str::FromStr for crate::VideoFieldOrder {
1344    type Err = glib::error::BoolError;
1345
1346    fn from_str(s: &str) -> Result<Self, Self::Err> {
1347        skip_assert_initialized!();
1348
1349        let fmt = Self::from_string(s);
1350        if fmt == Self::Unknown {
1351            Err(glib::bool_error!(
1352                "Failed to parse video field order from string"
1353            ))
1354        } else {
1355            Ok(fmt)
1356        }
1357    }
1358}
1359
1360impl str::FromStr for crate::VideoInterlaceMode {
1361    type Err = glib::error::BoolError;
1362
1363    fn from_str(s: &str) -> Result<Self, Self::Err> {
1364        skip_assert_initialized!();
1365
1366        let fmt = Self::from_string(s);
1367        Ok(fmt)
1368    }
1369}
1370
1371#[cfg(test)]
1372mod tests {
1373    use super::*;
1374
1375    #[test]
1376    fn test_new() {
1377        gst::init().unwrap();
1378
1379        let info = VideoInfo::builder(crate::VideoFormat::I420, 320, 240)
1380            .build()
1381            .unwrap();
1382        assert_eq!(info.format(), crate::VideoFormat::I420);
1383        assert_eq!(info.width(), 320);
1384        assert_eq!(info.height(), 240);
1385        assert_eq!(info.size(), 320 * 240 + 2 * 160 * 120);
1386        assert_eq!(info.multiview_mode(), crate::VideoMultiviewMode::None);
1387        assert_eq!(&info.offset(), &[0, 320 * 240, 320 * 240 + 160 * 120]);
1388        assert_eq!(&info.stride(), &[320, 160, 160]);
1389
1390        let offsets = [0, 640 * 240 + 16, 640 * 240 + 16 + 320 * 120 + 16];
1391        let strides = [640, 320, 320];
1392        let info = VideoInfo::builder(crate::VideoFormat::I420, 320, 240)
1393            .offset(&offsets)
1394            .stride(&strides)
1395            .size(640 * 240 + 16 + 320 * 120 + 16 + 320 * 120 + 16)
1396            .multiview_mode(crate::VideoMultiviewMode::SideBySide)
1397            .build()
1398            .unwrap();
1399        assert_eq!(info.format(), crate::VideoFormat::I420);
1400        assert_eq!(info.width(), 320);
1401        assert_eq!(info.height(), 240);
1402        assert_eq!(
1403            info.size(),
1404            640 * 240 + 16 + 320 * 120 + 16 + 320 * 120 + 16
1405        );
1406        assert_eq!(info.multiview_mode(), crate::VideoMultiviewMode::SideBySide);
1407        assert_eq!(
1408            &info.offset(),
1409            &[0, 640 * 240 + 16, 640 * 240 + 16 + 320 * 120 + 16]
1410        );
1411        assert_eq!(&info.stride(), &[640, 320, 320]);
1412    }
1413
1414    #[test]
1415    fn test_from_to_caps() {
1416        gst::init().unwrap();
1417
1418        let caps = crate::VideoCapsBuilder::new()
1419            .format(crate::VideoFormat::I420)
1420            .width(320)
1421            .height(240)
1422            .framerate((30, 1).into())
1423            .pixel_aspect_ratio((1, 1).into())
1424            .field("interlace-mode", "progressive")
1425            .field("chroma-site", "mpeg2")
1426            .field("colorimetry", "bt709")
1427            .build();
1428        let info = VideoInfo::from_caps(&caps).unwrap();
1429        assert_eq!(info.format(), crate::VideoFormat::I420);
1430        assert_eq!(info.width(), 320);
1431        assert_eq!(info.height(), 240);
1432        assert_eq!(info.fps(), gst::Fraction::new(30, 1));
1433        assert_eq!(
1434            info.interlace_mode(),
1435            crate::VideoInterlaceMode::Progressive
1436        );
1437        assert_eq!(info.chroma_site(), crate::VideoChromaSite::MPEG2);
1438        assert_eq!(info.colorimetry(), "bt709".parse().unwrap());
1439
1440        let caps2 = info.to_caps().unwrap();
1441        assert_eq!(caps, caps2);
1442
1443        let info2 = VideoInfo::from_caps(&caps2).unwrap();
1444        assert!(info == info2);
1445    }
1446
1447    #[test]
1448    fn test_video_align() {
1449        gst::init().unwrap();
1450
1451        let mut info = crate::VideoInfo::builder(crate::VideoFormat::Nv16, 1920, 1080)
1452            .build()
1453            .expect("Failed to create VideoInfo");
1454
1455        assert_eq!(info.stride(), [1920, 1920]);
1456        assert_eq!(info.offset(), [0, 2_073_600]);
1457
1458        let mut align = crate::VideoAlignment::new(0, 0, 0, 8, &[0; VIDEO_MAX_PLANES]);
1459        info.align(&mut align).unwrap();
1460
1461        assert_eq!(info.stride(), [1928, 1928]);
1462        assert_eq!(info.offset(), [0, 2_082_240]);
1463
1464        #[cfg(feature = "v1_18")]
1465        {
1466            let mut info = crate::VideoInfo::builder(crate::VideoFormat::Nv16, 1920, 1080)
1467                .build()
1468                .expect("Failed to create VideoInfo");
1469
1470            let mut align = crate::VideoAlignment::new(0, 0, 0, 8, &[0; VIDEO_MAX_PLANES]);
1471            let plane_size = info.align_full(&mut align).unwrap();
1472            assert_eq!(plane_size, [2082240, 2082240, 0, 0]);
1473        }
1474    }
1475
1476    #[test]
1477    fn test_display() {
1478        gst::init().unwrap();
1479
1480        let _ = format!("{}", "sRGB".parse::<crate::VideoColorimetry>().unwrap());
1481        let _ = format!("{}", crate::VideoFieldOrder::TopFieldFirst);
1482        let _ = format!("{}", crate::VideoInterlaceMode::Progressive);
1483    }
1484}