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