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