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