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