Skip to main content

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