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