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