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