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