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