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<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                info.offset[..offset.len()].copy_from_slice(offset);
408            }
409
410            if let Some(stride) = self.stride {
411                info.stride[..stride.len()].copy_from_slice(stride);
412            }
413
414            if let Some(multiview_mode) = self.multiview_mode {
415                info.ABI.abi.multiview_mode = multiview_mode.into_glib();
416            }
417
418            if let Some(multiview_flags) = self.multiview_flags {
419                info.ABI.abi.multiview_flags = multiview_flags.into_glib();
420            }
421
422            if let Some(field_order) = self.field_order {
423                info.ABI.abi.field_order = field_order.into_glib();
424            }
425
426            Ok(VideoInfo(info))
427        }
428    }
429
430    pub fn interlace_mode(self, interlace_mode: crate::VideoInterlaceMode) -> VideoInfoBuilder<'a> {
431        Self {
432            interlace_mode: Some(interlace_mode),
433            ..self
434        }
435    }
436
437    pub fn interlace_mode_if(
438        self,
439        interlace_mode: crate::VideoInterlaceMode,
440        predicate: bool,
441    ) -> VideoInfoBuilder<'a> {
442        if predicate {
443            self.interlace_mode(interlace_mode)
444        } else {
445            self
446        }
447    }
448
449    pub fn interlace_mode_if_some(
450        self,
451        interlace_mode: Option<crate::VideoInterlaceMode>,
452    ) -> VideoInfoBuilder<'a> {
453        if let Some(interlace_mode) = interlace_mode {
454            self.interlace_mode(interlace_mode)
455        } else {
456            self
457        }
458    }
459
460    pub fn flags(self, flags: crate::VideoFlags) -> Self {
461        Self {
462            flags: Some(flags),
463            ..self
464        }
465    }
466
467    pub fn flags_if(self, flags: crate::VideoFlags, predicate: bool) -> Self {
468        if predicate {
469            self.flags(flags)
470        } else {
471            self
472        }
473    }
474
475    pub fn flags_if_some(self, flags: Option<crate::VideoFlags>) -> Self {
476        if let Some(flags) = flags {
477            self.flags(flags)
478        } else {
479            self
480        }
481    }
482
483    pub fn size(self, size: usize) -> Self {
484        Self {
485            size: Some(size),
486            ..self
487        }
488    }
489
490    pub fn size_if(self, size: usize, predicate: bool) -> Self {
491        if predicate {
492            self.size(size)
493        } else {
494            self
495        }
496    }
497
498    pub fn size_if_some(self, size: Option<usize>) -> Self {
499        if let Some(size) = size {
500            self.size(size)
501        } else {
502            self
503        }
504    }
505
506    pub fn views(self, views: u32) -> Self {
507        Self {
508            views: Some(views),
509            ..self
510        }
511    }
512
513    pub fn views_if(self, views: u32, predicate: bool) -> Self {
514        if predicate {
515            self.views(views)
516        } else {
517            self
518        }
519    }
520
521    pub fn views_if_some(self, views: Option<u32>) -> Self {
522        if let Some(views) = views {
523            self.views(views)
524        } else {
525            self
526        }
527    }
528
529    pub fn chroma_site(self, chroma_site: crate::VideoChromaSite) -> Self {
530        Self {
531            chroma_site: Some(chroma_site),
532            ..self
533        }
534    }
535
536    pub fn chroma_site_if(self, chroma_site: crate::VideoChromaSite, predicate: bool) -> Self {
537        if predicate {
538            self.chroma_site(chroma_site)
539        } else {
540            self
541        }
542    }
543
544    pub fn chroma_site_if_some(self, chroma_site: Option<crate::VideoChromaSite>) -> Self {
545        if let Some(chroma_site) = chroma_site {
546            self.chroma_site(chroma_site)
547        } else {
548            self
549        }
550    }
551
552    pub fn colorimetry(self, colorimetry: &crate::VideoColorimetry) -> VideoInfoBuilder<'a> {
553        Self {
554            colorimetry: Some(*colorimetry),
555            ..self
556        }
557    }
558
559    pub fn colorimetry_if(
560        self,
561        colorimetry: &crate::VideoColorimetry,
562        predicate: bool,
563    ) -> VideoInfoBuilder<'a> {
564        if predicate {
565            self.colorimetry(colorimetry)
566        } else {
567            self
568        }
569    }
570
571    pub fn colorimetry_if_some(
572        self,
573        colorimetry: Option<&crate::VideoColorimetry>,
574    ) -> VideoInfoBuilder<'a> {
575        if let Some(colorimetry) = colorimetry {
576            self.colorimetry(colorimetry)
577        } else {
578            self
579        }
580    }
581
582    pub fn par<T: Into<gst::Fraction>>(self, par: T) -> Self {
583        Self {
584            par: Some(par.into()),
585            ..self
586        }
587    }
588
589    pub fn par_if<T: Into<gst::Fraction>>(self, par: T, predicate: bool) -> Self {
590        if predicate {
591            self.par(par)
592        } else {
593            self
594        }
595    }
596
597    pub fn par_if_some<T: Into<gst::Fraction>>(self, par: Option<T>) -> Self {
598        if let Some(par) = par {
599            self.par(par)
600        } else {
601            self
602        }
603    }
604
605    pub fn fps<T: Into<gst::Fraction>>(self, fps: T) -> Self {
606        Self {
607            fps: Some(fps.into()),
608            ..self
609        }
610    }
611
612    pub fn fps_if<T: Into<gst::Fraction>>(self, fps: T, predicate: bool) -> Self {
613        if predicate {
614            self.fps(fps)
615        } else {
616            self
617        }
618    }
619
620    pub fn fps_if_some<T: Into<gst::Fraction>>(self, fps: Option<T>) -> Self {
621        if let Some(fps) = fps {
622            self.fps(fps)
623        } else {
624            self
625        }
626    }
627
628    pub fn offset(self, offset: &'a [usize]) -> VideoInfoBuilder<'a> {
629        Self {
630            offset: Some(offset),
631            ..self
632        }
633    }
634
635    pub fn offset_if(self, offset: &'a [usize], predicate: bool) -> VideoInfoBuilder<'a> {
636        if predicate {
637            self.offset(offset)
638        } else {
639            self
640        }
641    }
642
643    pub fn offset_if_some(self, offset: Option<&'a [usize]>) -> VideoInfoBuilder<'a> {
644        if let Some(offset) = offset {
645            self.offset(offset)
646        } else {
647            self
648        }
649    }
650
651    pub fn stride(self, stride: &'a [i32]) -> VideoInfoBuilder<'a> {
652        Self {
653            stride: Some(stride),
654            ..self
655        }
656    }
657
658    pub fn stride_if(self, stride: &'a [i32], predicate: bool) -> VideoInfoBuilder<'a> {
659        if predicate {
660            self.stride(stride)
661        } else {
662            self
663        }
664    }
665
666    pub fn stride_if_some(self, stride: Option<&'a [i32]>) -> VideoInfoBuilder<'a> {
667        if let Some(stride) = stride {
668            self.stride(stride)
669        } else {
670            self
671        }
672    }
673
674    pub fn multiview_mode(self, multiview_mode: crate::VideoMultiviewMode) -> Self {
675        Self {
676            multiview_mode: Some(multiview_mode),
677            ..self
678        }
679    }
680
681    pub fn multiview_mode_if(
682        self,
683        multiview_mode: crate::VideoMultiviewMode,
684        predicate: bool,
685    ) -> Self {
686        if predicate {
687            self.multiview_mode(multiview_mode)
688        } else {
689            self
690        }
691    }
692
693    pub fn multiview_mode_if_some(self, multiview_mode: Option<crate::VideoMultiviewMode>) -> Self {
694        if let Some(multiview_mode) = multiview_mode {
695            self.multiview_mode(multiview_mode)
696        } else {
697            self
698        }
699    }
700
701    pub fn multiview_flags(self, multiview_flags: crate::VideoMultiviewFlags) -> Self {
702        Self {
703            multiview_flags: Some(multiview_flags),
704            ..self
705        }
706    }
707
708    pub fn multiview_flags_if(
709        self,
710        multiview_flags: crate::VideoMultiviewFlags,
711        predicate: bool,
712    ) -> Self {
713        if predicate {
714            self.multiview_flags(multiview_flags)
715        } else {
716            self
717        }
718    }
719
720    pub fn multiview_flags_if_some(
721        self,
722        multiview_flags: Option<crate::VideoMultiviewFlags>,
723    ) -> Self {
724        if let Some(multiview_flags) = multiview_flags {
725            self.multiview_flags(multiview_flags)
726        } else {
727            self
728        }
729    }
730
731    pub fn field_order(self, field_order: crate::VideoFieldOrder) -> Self {
732        Self {
733            field_order: Some(field_order),
734            ..self
735        }
736    }
737
738    pub fn field_order_if(self, field_order: crate::VideoFieldOrder, predicate: bool) -> Self {
739        if predicate {
740            self.field_order(field_order)
741        } else {
742            self
743        }
744    }
745
746    pub fn field_order_if_some(self, field_order: Option<crate::VideoFieldOrder>) -> Self {
747        if let Some(field_order) = field_order {
748            self.field_order(field_order)
749        } else {
750            self
751        }
752    }
753}
754
755impl VideoInfo {
756    pub fn builder<'a>(
757        format: crate::VideoFormat,
758        width: u32,
759        height: u32,
760    ) -> VideoInfoBuilder<'a> {
761        assert_initialized_main_thread!();
762
763        VideoInfoBuilder {
764            format,
765            width,
766            height,
767            interlace_mode: None,
768            flags: None,
769            size: None,
770            views: None,
771            chroma_site: None,
772            colorimetry: None,
773            par: None,
774            fps: None,
775            offset: None,
776            stride: None,
777            multiview_mode: None,
778            multiview_flags: None,
779            field_order: None,
780        }
781    }
782
783    pub fn builder_from_info(info: &VideoInfo) -> VideoInfoBuilder<'_> {
784        assert_initialized_main_thread!();
785
786        VideoInfoBuilder {
787            format: info.format(),
788            width: info.width(),
789            height: info.height(),
790            interlace_mode: Some(info.interlace_mode()),
791            flags: Some(info.flags()),
792            size: Some(info.size()),
793            views: Some(info.views()),
794            chroma_site: Some(info.chroma_site()),
795            colorimetry: Some(info.colorimetry()),
796            par: Some(info.par()),
797            fps: Some(info.fps()),
798            offset: Some(info.offset()),
799            stride: Some(info.stride()),
800            multiview_mode: Some(info.multiview_mode()),
801            multiview_flags: Some(info.multiview_flags()),
802            field_order: Some(info.field_order()),
803        }
804    }
805
806    #[inline]
807    pub fn is_valid(&self) -> bool {
808        !self.0.finfo.is_null()
809            && self.0.width > 0
810            && self.0.height > 0
811            && (self.0.size > 0 || unsafe { (*self.0.finfo).n_planes } == 0)
812    }
813
814    #[doc(alias = "gst_video_info_from_caps")]
815    pub fn from_caps(caps: &gst::CapsRef) -> Result<Self, glib::error::BoolError> {
816        skip_assert_initialized!();
817
818        unsafe {
819            let mut info = mem::MaybeUninit::uninit();
820            if from_glib(ffi::gst_video_info_from_caps(
821                info.as_mut_ptr(),
822                caps.as_ptr(),
823            )) {
824                Ok(Self(info.assume_init()))
825            } else {
826                Err(glib::bool_error!("Failed to create VideoInfo from caps"))
827            }
828        }
829    }
830
831    #[doc(alias = "gst_video_info_to_caps")]
832    pub fn to_caps(&self) -> Result<gst::Caps, glib::error::BoolError> {
833        unsafe {
834            let result = from_glib_full(ffi::gst_video_info_to_caps(mut_override(&self.0)));
835            match result {
836                Some(c) => Ok(c),
837                None => Err(glib::bool_error!("Failed to create caps from VideoInfo")),
838            }
839        }
840    }
841
842    #[inline]
843    pub fn format(&self) -> crate::VideoFormat {
844        if self.0.finfo.is_null() {
845            return crate::VideoFormat::Unknown;
846        }
847
848        self.format_info().format()
849    }
850
851    #[inline]
852    pub fn format_info(&self) -> crate::VideoFormatInfo {
853        unsafe { crate::VideoFormatInfo::from_ptr(self.0.finfo) }
854    }
855
856    #[inline]
857    pub fn name<'a>(&self) -> &'a str {
858        self.format_info().name()
859    }
860
861    #[inline]
862    pub fn width(&self) -> u32 {
863        self.0.width as u32
864    }
865
866    #[inline]
867    pub fn height(&self) -> u32 {
868        self.0.height as u32
869    }
870
871    #[cfg(feature = "v1_16")]
872    #[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
873    #[inline]
874    pub fn field_height(&self) -> u32 {
875        if self.0.interlace_mode == ffi::GST_VIDEO_INTERLACE_MODE_ALTERNATE {
876            (self.0.height as u32).div_ceil(2)
877        } else {
878            self.0.height as u32
879        }
880    }
881
882    #[inline]
883    pub fn interlace_mode(&self) -> crate::VideoInterlaceMode {
884        unsafe { from_glib(self.0.interlace_mode) }
885    }
886
887    #[inline]
888    pub fn flags(&self) -> crate::VideoFlags {
889        unsafe { from_glib(self.0.flags) }
890    }
891
892    #[inline]
893    pub fn size(&self) -> usize {
894        self.0.size
895    }
896
897    #[inline]
898    pub fn views(&self) -> u32 {
899        self.0.views as u32
900    }
901
902    #[inline]
903    pub fn chroma_site(&self) -> crate::VideoChromaSite {
904        unsafe { from_glib(self.0.chroma_site) }
905    }
906
907    #[inline]
908    pub fn colorimetry(&self) -> VideoColorimetry {
909        unsafe { VideoColorimetry(ptr::read(&self.0.colorimetry)) }
910    }
911
912    #[inline]
913    pub fn comp_depth(&self, component: u8) -> u32 {
914        self.format_info().depth()[component as usize]
915    }
916
917    #[inline]
918    pub fn comp_height(&self, component: u8) -> u32 {
919        self.format_info().scale_height(component, self.height())
920    }
921
922    #[inline]
923    pub fn comp_width(&self, component: u8) -> u32 {
924        self.format_info().scale_width(component, self.width())
925    }
926
927    #[inline]
928    pub fn comp_offset(&self, component: u8) -> usize {
929        self.offset()[self.format_info().plane()[component as usize] as usize]
930            + self.format_info().poffset()[component as usize] as usize
931    }
932
933    #[inline]
934    pub fn comp_plane(&self, component: u8) -> u32 {
935        self.format_info().plane()[component as usize]
936    }
937
938    #[inline]
939    pub fn comp_poffset(&self, component: u8) -> u32 {
940        self.format_info().poffset()[component as usize]
941    }
942
943    #[inline]
944    pub fn comp_pstride(&self, component: u8) -> i32 {
945        self.format_info().pixel_stride()[component as usize]
946    }
947
948    #[inline]
949    pub fn comp_stride(&self, component: u8) -> i32 {
950        self.stride()[self.format_info().plane()[component as usize] as usize]
951    }
952
953    #[inline]
954    pub fn par(&self) -> gst::Fraction {
955        gst::Fraction::new(self.0.par_n, self.0.par_d)
956    }
957
958    #[inline]
959    pub fn fps(&self) -> gst::Fraction {
960        gst::Fraction::new(self.0.fps_n, self.0.fps_d)
961    }
962
963    #[cfg(feature = "v1_16")]
964    #[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
965    #[inline]
966    pub fn field_rate(&self) -> gst::Fraction {
967        if self.interlace_mode() == crate::VideoInterlaceMode::Alternate {
968            2 * self.fps()
969        } else {
970            self.fps()
971        }
972    }
973
974    #[inline]
975    pub fn offset(&self) -> &[usize] {
976        &self.0.offset[0..(self.format_info().n_planes() as usize)]
977    }
978
979    #[inline]
980    pub fn stride(&self) -> &[i32] {
981        &self.0.stride[0..(self.format_info().n_planes() as usize)]
982    }
983
984    #[inline]
985    pub fn multiview_mode(&self) -> crate::VideoMultiviewMode {
986        unsafe {
987            let ptr = &self.0.ABI._gst_reserved as *const _ as *const i32;
988            from_glib(ptr::read(ptr.offset(0)))
989        }
990    }
991
992    #[inline]
993    pub fn multiview_flags(&self) -> crate::VideoMultiviewFlags {
994        unsafe {
995            let ptr = &self.0.ABI._gst_reserved as *const _ as *const u32;
996            from_glib(ptr::read(ptr.offset(1)))
997        }
998    }
999
1000    #[inline]
1001    pub fn field_order(&self) -> crate::VideoFieldOrder {
1002        unsafe {
1003            let ptr = &self.0.ABI._gst_reserved as *const _ as *const i32;
1004            from_glib(ptr::read(ptr.offset(2)))
1005        }
1006    }
1007
1008    #[inline]
1009    pub fn has_alpha(&self) -> bool {
1010        self.format_info().has_alpha()
1011    }
1012
1013    #[inline]
1014    pub fn is_gray(&self) -> bool {
1015        self.format_info().is_gray()
1016    }
1017
1018    #[inline]
1019    pub fn is_rgb(&self) -> bool {
1020        self.format_info().is_rgb()
1021    }
1022
1023    #[inline]
1024    pub fn is_yuv(&self) -> bool {
1025        self.format_info().is_yuv()
1026    }
1027
1028    #[inline]
1029    pub fn is_interlaced(&self) -> bool {
1030        self.interlace_mode() != crate::VideoInterlaceMode::Progressive
1031    }
1032
1033    #[inline]
1034    pub fn n_planes(&self) -> u32 {
1035        self.format_info().n_planes()
1036    }
1037
1038    #[inline]
1039    pub fn n_components(&self) -> u32 {
1040        self.format_info().n_components()
1041    }
1042
1043    #[doc(alias = "gst_video_info_convert")]
1044    pub fn convert<U: gst::format::SpecificFormattedValueFullRange>(
1045        &self,
1046        src_val: impl gst::format::FormattedValue,
1047    ) -> Option<U> {
1048        skip_assert_initialized!();
1049        unsafe {
1050            let mut dest_val = mem::MaybeUninit::uninit();
1051            if from_glib(ffi::gst_video_info_convert(
1052                mut_override(&self.0),
1053                src_val.format().into_glib(),
1054                src_val.into_raw_value(),
1055                U::default_format().into_glib(),
1056                dest_val.as_mut_ptr(),
1057            )) {
1058                Some(U::from_raw(U::default_format(), dest_val.assume_init()))
1059            } else {
1060                None
1061            }
1062        }
1063    }
1064
1065    pub fn convert_generic(
1066        &self,
1067        src_val: impl gst::format::FormattedValue,
1068        dest_fmt: gst::Format,
1069    ) -> Option<gst::GenericFormattedValue> {
1070        skip_assert_initialized!();
1071        unsafe {
1072            let mut dest_val = mem::MaybeUninit::uninit();
1073            if from_glib(ffi::gst_video_info_convert(
1074                mut_override(&self.0),
1075                src_val.format().into_glib(),
1076                src_val.into_raw_value(),
1077                dest_fmt.into_glib(),
1078                dest_val.as_mut_ptr(),
1079            )) {
1080                Some(gst::GenericFormattedValue::new(
1081                    dest_fmt,
1082                    dest_val.assume_init(),
1083                ))
1084            } else {
1085                None
1086            }
1087        }
1088    }
1089
1090    #[doc(alias = "gst_video_info_align")]
1091    pub fn align(&mut self, align: &mut crate::VideoAlignment) -> Result<(), glib::BoolError> {
1092        unsafe {
1093            glib::result_from_gboolean!(
1094                ffi::gst_video_info_align(&mut self.0, &mut align.0,),
1095                "Failed to align VideoInfo"
1096            )
1097        }
1098    }
1099
1100    #[cfg(feature = "v1_18")]
1101    #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
1102    #[doc(alias = "gst_video_info_align_full")]
1103    pub fn align_full(
1104        &mut self,
1105        align: &mut crate::VideoAlignment,
1106    ) -> Result<[usize; crate::VIDEO_MAX_PLANES], glib::BoolError> {
1107        let mut plane_size = [0; crate::VIDEO_MAX_PLANES];
1108
1109        unsafe {
1110            glib::result_from_gboolean!(
1111                ffi::gst_video_info_align_full(&mut self.0, &mut align.0, plane_size.as_mut_ptr()),
1112                "Failed to align VideoInfo"
1113            )?;
1114        }
1115
1116        Ok(plane_size)
1117    }
1118
1119    #[doc(alias = "gst_video_color_range_offsets")]
1120    #[inline]
1121    pub fn range_offsets(&self, range: crate::VideoColorRange) -> ([i32; 4], [i32; 4]) {
1122        self.format_info().range_offsets(range)
1123    }
1124}
1125
1126impl PartialEq for VideoInfo {
1127    #[doc(alias = "gst_video_info_is_equal")]
1128    fn eq(&self, other: &Self) -> bool {
1129        unsafe { from_glib(ffi::gst_video_info_is_equal(&self.0, &other.0)) }
1130    }
1131}
1132
1133impl Eq for VideoInfo {}
1134
1135unsafe impl Send for VideoInfo {}
1136unsafe impl Sync for VideoInfo {}
1137
1138impl glib::types::StaticType for VideoInfo {
1139    #[inline]
1140    fn static_type() -> glib::types::Type {
1141        unsafe { glib::translate::from_glib(ffi::gst_video_info_get_type()) }
1142    }
1143}
1144
1145impl glib::value::ValueType for VideoInfo {
1146    type Type = Self;
1147}
1148
1149#[doc(hidden)]
1150unsafe impl<'a> glib::value::FromValue<'a> for VideoInfo {
1151    type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
1152
1153    unsafe fn from_value(value: &'a glib::Value) -> Self {
1154        skip_assert_initialized!();
1155        from_glib_none(
1156            glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut ffi::GstVideoInfo
1157        )
1158    }
1159}
1160
1161#[doc(hidden)]
1162impl glib::value::ToValue for VideoInfo {
1163    fn to_value(&self) -> glib::Value {
1164        let mut value = glib::Value::for_value_type::<Self>();
1165        unsafe {
1166            glib::gobject_ffi::g_value_set_boxed(
1167                value.to_glib_none_mut().0,
1168                self.to_glib_none().0 as *mut _,
1169            )
1170        }
1171        value
1172    }
1173
1174    fn value_type(&self) -> glib::Type {
1175        Self::static_type()
1176    }
1177}
1178
1179#[doc(hidden)]
1180impl glib::value::ToValueOptional for VideoInfo {
1181    fn to_value_optional(s: Option<&Self>) -> glib::Value {
1182        skip_assert_initialized!();
1183        let mut value = glib::Value::for_value_type::<Self>();
1184        unsafe {
1185            glib::gobject_ffi::g_value_set_boxed(
1186                value.to_glib_none_mut().0,
1187                s.to_glib_none().0 as *mut _,
1188            )
1189        }
1190        value
1191    }
1192}
1193
1194#[doc(hidden)]
1195impl From<VideoInfo> for glib::Value {
1196    fn from(v: VideoInfo) -> glib::Value {
1197        skip_assert_initialized!();
1198        glib::value::ToValue::to_value(&v)
1199    }
1200}
1201
1202#[doc(hidden)]
1203impl glib::translate::Uninitialized for VideoInfo {
1204    #[inline]
1205    unsafe fn uninitialized() -> Self {
1206        mem::zeroed()
1207    }
1208}
1209
1210#[doc(hidden)]
1211impl glib::translate::GlibPtrDefault for VideoInfo {
1212    type GlibType = *mut ffi::GstVideoInfo;
1213}
1214
1215#[doc(hidden)]
1216impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstVideoInfo> for VideoInfo {
1217    type Storage = PhantomData<&'a Self>;
1218
1219    #[inline]
1220    fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstVideoInfo, Self> {
1221        glib::translate::Stash(&self.0, PhantomData)
1222    }
1223
1224    fn to_glib_full(&self) -> *const ffi::GstVideoInfo {
1225        unimplemented!()
1226    }
1227}
1228
1229#[doc(hidden)]
1230impl glib::translate::FromGlibPtrNone<*const ffi::GstVideoInfo> for VideoInfo {
1231    #[inline]
1232    unsafe fn from_glib_none(ptr: *const ffi::GstVideoInfo) -> Self {
1233        Self(ptr::read(ptr))
1234    }
1235}
1236
1237#[doc(hidden)]
1238impl glib::translate::FromGlibPtrNone<*mut ffi::GstVideoInfo> for VideoInfo {
1239    #[inline]
1240    unsafe fn from_glib_none(ptr: *mut ffi::GstVideoInfo) -> Self {
1241        Self(ptr::read(ptr))
1242    }
1243}
1244
1245#[doc(hidden)]
1246impl glib::translate::FromGlibPtrFull<*mut ffi::GstVideoInfo> for VideoInfo {
1247    #[inline]
1248    unsafe fn from_glib_full(ptr: *mut ffi::GstVideoInfo) -> Self {
1249        let info = from_glib_none(ptr);
1250        glib::ffi::g_free(ptr as *mut _);
1251        info
1252    }
1253}
1254
1255impl crate::VideoFieldOrder {
1256    #[doc(alias = "gst_video_field_order_to_string")]
1257    pub fn to_str<'a>(self) -> &'a str {
1258        use std::ffi::CStr;
1259
1260        if self == Self::Unknown {
1261            return "UNKNOWN";
1262        }
1263        unsafe {
1264            CStr::from_ptr(
1265                ffi::gst_video_field_order_to_string(self.into_glib())
1266                    .as_ref()
1267                    .expect("gst_video_field_order_to_string returned NULL"),
1268            )
1269            .to_str()
1270            .expect("gst_video_field_order_to_string returned an invalid string")
1271        }
1272    }
1273}
1274
1275impl str::FromStr for crate::VideoFieldOrder {
1276    type Err = glib::error::BoolError;
1277
1278    fn from_str(s: &str) -> Result<Self, Self::Err> {
1279        skip_assert_initialized!();
1280
1281        let fmt = Self::from_string(s);
1282        if fmt == Self::Unknown {
1283            Err(glib::bool_error!(
1284                "Failed to parse video field order from string"
1285            ))
1286        } else {
1287            Ok(fmt)
1288        }
1289    }
1290}
1291
1292impl str::FromStr for crate::VideoInterlaceMode {
1293    type Err = glib::error::BoolError;
1294
1295    fn from_str(s: &str) -> Result<Self, Self::Err> {
1296        skip_assert_initialized!();
1297
1298        let fmt = Self::from_string(s);
1299        Ok(fmt)
1300    }
1301}
1302
1303#[cfg(test)]
1304mod tests {
1305    use super::*;
1306
1307    #[test]
1308    fn test_new() {
1309        gst::init().unwrap();
1310
1311        let info = VideoInfo::builder(crate::VideoFormat::I420, 320, 240)
1312            .build()
1313            .unwrap();
1314        assert_eq!(info.format(), crate::VideoFormat::I420);
1315        assert_eq!(info.width(), 320);
1316        assert_eq!(info.height(), 240);
1317        assert_eq!(info.size(), 320 * 240 + 2 * 160 * 120);
1318        assert_eq!(info.multiview_mode(), crate::VideoMultiviewMode::None);
1319        assert_eq!(&info.offset(), &[0, 320 * 240, 320 * 240 + 160 * 120]);
1320        assert_eq!(&info.stride(), &[320, 160, 160]);
1321
1322        let offsets = [0, 640 * 240 + 16, 640 * 240 + 16 + 320 * 120 + 16];
1323        let strides = [640, 320, 320];
1324        let info = VideoInfo::builder(crate::VideoFormat::I420, 320, 240)
1325            .offset(&offsets)
1326            .stride(&strides)
1327            .size(640 * 240 + 16 + 320 * 120 + 16 + 320 * 120 + 16)
1328            .multiview_mode(crate::VideoMultiviewMode::SideBySide)
1329            .build()
1330            .unwrap();
1331        assert_eq!(info.format(), crate::VideoFormat::I420);
1332        assert_eq!(info.width(), 320);
1333        assert_eq!(info.height(), 240);
1334        assert_eq!(
1335            info.size(),
1336            640 * 240 + 16 + 320 * 120 + 16 + 320 * 120 + 16
1337        );
1338        assert_eq!(info.multiview_mode(), crate::VideoMultiviewMode::SideBySide);
1339        assert_eq!(
1340            &info.offset(),
1341            &[0, 640 * 240 + 16, 640 * 240 + 16 + 320 * 120 + 16]
1342        );
1343        assert_eq!(&info.stride(), &[640, 320, 320]);
1344    }
1345
1346    #[test]
1347    fn test_from_to_caps() {
1348        gst::init().unwrap();
1349
1350        let caps = crate::VideoCapsBuilder::new()
1351            .format(crate::VideoFormat::I420)
1352            .width(320)
1353            .height(240)
1354            .framerate((30, 1).into())
1355            .pixel_aspect_ratio((1, 1).into())
1356            .field("interlace-mode", "progressive")
1357            .field("chroma-site", "mpeg2")
1358            .field("colorimetry", "bt709")
1359            .build();
1360        let info = VideoInfo::from_caps(&caps).unwrap();
1361        assert_eq!(info.format(), crate::VideoFormat::I420);
1362        assert_eq!(info.width(), 320);
1363        assert_eq!(info.height(), 240);
1364        assert_eq!(info.fps(), gst::Fraction::new(30, 1));
1365        assert_eq!(
1366            info.interlace_mode(),
1367            crate::VideoInterlaceMode::Progressive
1368        );
1369        assert_eq!(info.chroma_site(), crate::VideoChromaSite::MPEG2);
1370        assert_eq!(info.colorimetry(), "bt709".parse().unwrap());
1371
1372        let caps2 = info.to_caps().unwrap();
1373        assert_eq!(caps, caps2);
1374
1375        let info2 = VideoInfo::from_caps(&caps2).unwrap();
1376        assert!(info == info2);
1377    }
1378
1379    #[test]
1380    fn test_video_align() {
1381        gst::init().unwrap();
1382
1383        let mut info = crate::VideoInfo::builder(crate::VideoFormat::Nv16, 1920, 1080)
1384            .build()
1385            .expect("Failed to create VideoInfo");
1386
1387        assert_eq!(info.stride(), [1920, 1920]);
1388        assert_eq!(info.offset(), [0, 2_073_600]);
1389
1390        let mut align = crate::VideoAlignment::new(0, 0, 0, 8, &[0; VIDEO_MAX_PLANES]);
1391        info.align(&mut align).unwrap();
1392
1393        assert_eq!(info.stride(), [1928, 1928]);
1394        assert_eq!(info.offset(), [0, 2_082_240]);
1395
1396        #[cfg(feature = "v1_18")]
1397        {
1398            let mut info = crate::VideoInfo::builder(crate::VideoFormat::Nv16, 1920, 1080)
1399                .build()
1400                .expect("Failed to create VideoInfo");
1401
1402            let mut align = crate::VideoAlignment::new(0, 0, 0, 8, &[0; VIDEO_MAX_PLANES]);
1403            let plane_size = info.align_full(&mut align).unwrap();
1404            assert_eq!(plane_size, [2082240, 2082240, 0, 0]);
1405        }
1406    }
1407
1408    #[test]
1409    fn test_display() {
1410        gst::init().unwrap();
1411
1412        let _ = format!("{}", "sRGB".parse::<crate::VideoColorimetry>().unwrap());
1413        let _ = format!("{}", crate::VideoFieldOrder::TopFieldFirst);
1414        let _ = format!("{}", crate::VideoInterlaceMode::Progressive);
1415    }
1416}