1use std::{cmp::Ordering, fmt, marker::PhantomData, str};
4
5use crate::ffi;
6use glib::translate::{from_glib, IntoGlib, ToGlibPtr};
7
8#[doc(alias = "GstVideoFormatInfo")]
10#[derive(Copy, Clone)]
11pub struct VideoFormatInfo(&'static ffi::GstVideoFormatInfo);
12
13impl VideoFormatInfo {
14 #[inline]
15 pub unsafe fn from_ptr(format_info: *const ffi::GstVideoFormatInfo) -> Self {
16 debug_assert!(!format_info.is_null());
17 Self(&*format_info)
18 }
19
20 #[inline]
21 pub fn from_format(format: crate::VideoFormat) -> Self {
22 assert_initialized_main_thread!();
23 unsafe {
24 let info = ffi::gst_video_format_get_info(format.into_glib());
25 debug_assert!(!info.is_null());
26
27 Self(&*info)
28 }
29 }
30
31 #[inline]
32 pub fn format(&self) -> crate::VideoFormat {
33 unsafe { from_glib(self.0.format) }
34 }
35
36 #[inline]
37 pub fn name<'a>(&self) -> &'a glib::GStr {
38 unsafe { glib::GStr::from_ptr(self.0.name) }
39 }
40
41 #[inline]
42 pub fn description<'a>(&self) -> &'a glib::GStr {
43 unsafe { glib::GStr::from_ptr(self.0.description) }
44 }
45
46 #[inline]
47 pub fn flags(&self) -> crate::VideoFormatFlags {
48 unsafe { from_glib(self.0.flags) }
49 }
50
51 #[inline]
52 pub fn bits(&self) -> u32 {
53 self.0.bits
54 }
55
56 #[inline]
57 pub fn n_components(&self) -> u32 {
58 self.0.n_components
59 }
60
61 #[inline]
62 pub fn shift(&self) -> &[u32] {
63 &self.0.shift[0..(self.0.n_components as usize)]
64 }
65
66 #[inline]
67 pub fn depth(&self) -> &[u32] {
68 &self.0.depth[0..(self.0.n_components as usize)]
69 }
70
71 #[inline]
72 pub fn pixel_stride(&self) -> &[i32] {
73 &self.0.pixel_stride[0..(self.0.n_components as usize)]
74 }
75
76 #[inline]
77 pub fn n_planes(&self) -> u32 {
78 self.0.n_planes
79 }
80
81 #[inline]
82 pub fn plane(&self) -> &[u32] {
83 &self.0.plane[0..(self.0.n_components as usize)]
84 }
85
86 #[inline]
87 pub fn poffset(&self) -> &[u32] {
88 &self.0.poffset[0..(self.0.n_components as usize)]
89 }
90
91 #[inline]
92 pub fn w_sub(&self) -> &[u32] {
93 &self.0.w_sub[0..(self.0.n_components as usize)]
94 }
95
96 #[inline]
97 pub fn h_sub(&self) -> &[u32] {
98 &self.0.h_sub[0..(self.0.n_components as usize)]
99 }
100
101 #[inline]
102 pub fn tile_mode(&self) -> crate::VideoTileMode {
103 unsafe { from_glib(self.0.tile_mode) }
104 }
105
106 #[cfg_attr(feature = "v1_22", deprecated = "Since 1.22")]
107 #[inline]
108 pub fn tile_ws(&self) -> u32 {
109 self.0.tile_ws
110 }
111
112 #[cfg_attr(feature = "v1_22", deprecated = "Since 1.22")]
113 #[inline]
114 pub fn tile_hs(&self) -> u32 {
115 self.0.tile_hs
116 }
117
118 #[inline]
119 pub fn unpack_format(&self) -> crate::VideoFormat {
120 unsafe { from_glib(self.0.unpack_format) }
121 }
122
123 #[inline]
124 pub fn pack_lines(&self) -> i32 {
125 self.0.pack_lines
126 }
127
128 #[inline]
129 pub fn has_alpha(&self) -> bool {
130 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_ALPHA != 0
131 }
132
133 #[inline]
134 pub fn has_palette(&self) -> bool {
135 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_PALETTE != 0
136 }
137
138 #[cfg(feature = "v1_22")]
139 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
140 #[inline]
141 pub fn has_subtiles(&self) -> bool {
142 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_SUBTILES != 0
143 }
144
145 #[inline]
146 pub fn is_complex(&self) -> bool {
147 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_COMPLEX != 0
148 }
149
150 #[inline]
151 pub fn is_gray(&self) -> bool {
152 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_GRAY != 0
153 }
154
155 #[inline]
156 pub fn is_le(&self) -> bool {
157 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_LE != 0
158 }
159
160 #[inline]
161 pub fn is_rgb(&self) -> bool {
162 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_RGB != 0
163 }
164
165 #[inline]
166 pub fn is_tiled(&self) -> bool {
167 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_TILED != 0
168 }
169
170 #[inline]
171 pub fn is_yuv(&self) -> bool {
172 self.0.flags & ffi::GST_VIDEO_FORMAT_FLAG_YUV != 0
173 }
174
175 #[inline]
176 pub fn scale_width(&self, component: u8, width: u32) -> u32 {
177 (-((-(i64::from(width))) >> self.w_sub()[component as usize])) as u32
178 }
179
180 #[inline]
181 pub fn scale_height(&self, component: u8, height: u32) -> u32 {
182 (-((-(i64::from(height))) >> self.h_sub()[component as usize])) as u32
183 }
184
185 #[allow(clippy::too_many_arguments)]
186 pub fn unpack(
187 &self,
188 flags: crate::VideoPackFlags,
189 dest: &mut [u8],
190 src: &[&[u8]],
191 stride: &[i32],
192 x: i32,
193 y: i32,
194 width: i32,
195 ) {
196 let unpack_format = Self::from_format(self.unpack_format());
197
198 if unpack_format.pixel_stride()[0] == 0 || self.0.unpack_func.is_none() {
199 panic!("No unpack format for {self:?}");
200 }
201
202 if src.len() != self.n_planes() as usize {
203 panic!(
204 "Wrong number of planes provided for format: {} != {}",
205 src.len(),
206 self.n_planes()
207 );
208 }
209
210 if stride.len() != self.n_planes() as usize {
211 panic!(
212 "Wrong number of strides provided for format: {} != {}",
213 stride.len(),
214 self.n_planes()
215 );
216 }
217
218 if dest.len() < unpack_format.pixel_stride()[0] as usize * width as usize {
219 panic!("Too small destination slice");
220 }
221
222 for plane in 0..(self.n_planes()) {
223 if stride[plane as usize]
224 < self.scale_width(plane as u8, width as u32) as i32
225 * self.pixel_stride()[plane as usize]
226 {
227 panic!("Too small source stride for plane {plane}");
228 }
229
230 let plane_size = y * stride[plane as usize]
231 + self.scale_width(plane as u8, (x + width) as u32) as i32
232 * self.pixel_stride()[plane as usize];
233
234 if src[plane as usize].len() < plane_size as usize {
235 panic!("Too small source plane size for plane {plane}");
236 }
237 }
238
239 unsafe {
240 use std::ptr;
241
242 let mut src_ptr = [ptr::null(); ffi::GST_VIDEO_MAX_PLANES as usize];
243 for plane in 0..(self.n_planes()) {
244 src_ptr[plane as usize] = src[plane as usize].as_ptr();
245 }
246
247 (self.0.unpack_func.as_ref().unwrap())(
248 self.0,
249 flags.into_glib(),
250 dest.as_mut_ptr() as *mut _,
251 src_ptr.as_ptr() as *const _,
252 stride.as_ptr(),
253 x,
254 y,
255 width,
256 );
257 }
258 }
259
260 #[allow(clippy::too_many_arguments)]
261 pub fn pack(
262 &self,
263 flags: crate::VideoPackFlags,
264 src: &[u8],
265 src_stride: i32,
266 dest: &mut [&mut [u8]],
267 dest_stride: &[i32],
268 chroma_site: crate::VideoChromaSite,
269 y: i32,
270 width: i32,
271 ) {
272 let unpack_format = Self::from_format(self.unpack_format());
273
274 if unpack_format.pixel_stride()[0] == 0 || self.0.unpack_func.is_none() {
275 panic!("No unpack format for {self:?}");
276 }
277
278 if dest.len() != self.n_planes() as usize {
279 panic!(
280 "Wrong number of planes provided for format: {} != {}",
281 dest.len(),
282 self.n_planes()
283 );
284 }
285
286 if dest_stride.len() != self.n_planes() as usize {
287 panic!(
288 "Wrong number of strides provided for format: {} != {}",
289 dest_stride.len(),
290 self.n_planes()
291 );
292 }
293
294 if src.len() < unpack_format.pixel_stride()[0] as usize * width as usize {
295 panic!("Too small source slice");
296 }
297
298 for plane in 0..(self.n_planes()) {
299 if dest_stride[plane as usize]
300 < self.scale_width(plane as u8, width as u32) as i32
301 * self.pixel_stride()[plane as usize]
302 {
303 panic!("Too small destination stride for plane {plane}");
304 }
305
306 let plane_size = y * dest_stride[plane as usize]
307 + self.scale_width(plane as u8, width as u32) as i32
308 * self.pixel_stride()[plane as usize];
309
310 if dest[plane as usize].len() < plane_size as usize {
311 panic!("Too small destination plane size for plane {plane}");
312 }
313 }
314
315 unsafe {
316 use std::ptr;
317
318 let mut dest_ptr = [ptr::null_mut(); ffi::GST_VIDEO_MAX_PLANES as usize];
319 for plane in 0..(self.n_planes()) {
320 dest_ptr[plane as usize] = dest[plane as usize].as_mut_ptr();
321 }
322
323 (self.0.pack_func.as_ref().unwrap())(
324 self.0,
325 flags.into_glib(),
326 src.as_ptr() as *mut _,
327 src_stride,
328 dest_ptr.as_mut_ptr() as *mut _,
329 dest_stride.as_ptr(),
330 chroma_site.into_glib(),
331 y,
332 width,
333 );
334 }
335 }
336
337 #[doc(alias = "gst_video_color_range_offsets")]
338 pub fn range_offsets(&self, range: crate::VideoColorRange) -> ([i32; 4], [i32; 4]) {
339 let mut offset = [0i32; 4];
340 let mut scale = [0i32; 4];
341 unsafe {
342 ffi::gst_video_color_range_offsets(
343 range.into_glib(),
344 self.to_glib_none().0,
345 &mut offset,
346 &mut scale,
347 )
348 }
349 (offset, scale)
350 }
351
352 #[cfg(feature = "v1_22")]
363 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
364 #[doc(alias = "gst_video_format_info_extrapolate_stride")]
365 pub fn extrapolate_stride(&self, plane: u32, stride: u32) -> u32 {
366 assert!(plane < self.n_planes());
367
368 unsafe {
369 ffi::gst_video_format_info_extrapolate_stride(
370 self.to_glib_none().0,
371 plane as i32,
372 stride as i32,
373 ) as u32
374 }
375 }
376
377 #[cfg(feature = "v1_22")]
378 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
379 pub fn tile_info(&self, plane: u32) -> &VideoTileInfo {
380 assert!(plane < self.n_planes());
381
382 unsafe { &*(&self.0.tile_info[plane as usize] as *const _ as *const VideoTileInfo) }
383 }
384
385 #[cfg(feature = "v1_18")]
397 #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
398 #[doc(alias = "gst_video_format_info_component")]
399 pub fn component(&self, plane: u32) -> [i32; ffi::GST_VIDEO_MAX_COMPONENTS as usize] {
400 assert!(plane < self.n_planes());
401
402 let mut comp = [-1i32; ffi::GST_VIDEO_MAX_COMPONENTS as usize];
403 unsafe {
404 ffi::gst_video_format_info_component(self.to_glib_none().0, plane, comp.as_mut_ptr());
405 }
406 comp
407 }
408}
409
410unsafe impl Sync for VideoFormatInfo {}
411unsafe impl Send for VideoFormatInfo {}
412
413impl PartialEq for VideoFormatInfo {
414 #[inline]
415 fn eq(&self, other: &Self) -> bool {
416 self.format() == other.format()
417 }
418}
419
420impl Eq for VideoFormatInfo {}
421
422impl PartialOrd for VideoFormatInfo {
423 #[inline]
424 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
425 Some(self.cmp(other))
426 }
427}
428
429impl Ord for VideoFormatInfo {
430 fn cmp(&self, other: &Self) -> Ordering {
432 self.n_components()
433 .cmp(&other.n_components())
434 .reverse()
435 .then_with(|| self.depth().cmp(other.depth()).reverse())
436 .then_with(|| self.w_sub().cmp(other.w_sub()))
437 .then_with(|| self.h_sub().cmp(other.h_sub()))
438 .then_with(|| self.n_planes().cmp(&other.n_planes()).reverse())
439 .then_with(|| {
440 let native_endianness = [crate::VideoFormat::Ayuv64, crate::VideoFormat::Argb64];
442 let want_le = cfg!(target_endian = "little");
443
444 match (
445 self.flags().contains(crate::VideoFormatFlags::LE) == want_le
446 || native_endianness.contains(&self.format()),
447 other.flags().contains(crate::VideoFormatFlags::LE) == want_le
448 || native_endianness.contains(&other.format()),
449 ) {
450 (true, false) => Ordering::Less,
451 (false, true) => Ordering::Greater,
452 _ => Ordering::Equal,
453 }
454 })
455 .then_with(|| {
456 match (
458 self.flags().contains(crate::VideoFormatFlags::COMPLEX),
459 other.flags().contains(crate::VideoFormatFlags::COMPLEX),
460 ) {
461 (true, false) => Ordering::Greater,
462 (false, true) => Ordering::Less,
463 _ => Ordering::Equal,
464 }
465 })
466 .then_with(|| {
467 if self.flags().contains(crate::VideoFormatFlags::RGB)
469 && other.flags().contains(crate::VideoFormatFlags::YUV)
470 {
471 Ordering::Greater
472 } else if self.flags().contains(crate::VideoFormatFlags::YUV)
473 && other.flags().contains(crate::VideoFormatFlags::RGB)
474 {
475 Ordering::Less
476 } else {
477 Ordering::Equal
478 }
479 })
480 .then_with(|| {
481 let xrgb = [
483 crate::VideoFormat::Xrgb,
484 crate::VideoFormat::Xbgr,
485 crate::VideoFormat::Rgbx,
486 crate::VideoFormat::Bgrx,
487 ];
488 let rgb = [crate::VideoFormat::Rgb, crate::VideoFormat::Bgr];
489
490 if xrgb.contains(&self.format()) && rgb.contains(&other.format()) {
491 Ordering::Less
492 } else if rgb.contains(&self.format()) && xrgb.contains(&other.format()) {
493 Ordering::Greater
494 } else {
495 Ordering::Equal
496 }
497 })
498 .then_with(|| self.pixel_stride().cmp(other.pixel_stride()))
499 .then_with(|| self.poffset().cmp(other.poffset()))
500 .then_with(|| {
501 self.name().cmp(other.name())
503 })
504 .reverse()
506 }
507}
508
509impl fmt::Debug for VideoFormatInfo {
510 #[allow(deprecated)]
511 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
512 let mut fmt = f.debug_struct("VideoFormatInfo");
513
514 fmt.field("format", &self.format())
515 .field("name", &self.name())
516 .field("description", &self.description())
517 .field("flags", &self.flags())
518 .field("bits", &self.bits())
519 .field("n-components", &self.n_components())
520 .field("shift", &self.shift())
521 .field("depth", &self.depth())
522 .field("pixel-stride", &self.pixel_stride())
523 .field("n-planes", &self.n_planes())
524 .field("plane", &self.plane())
525 .field("poffset", &self.poffset())
526 .field("w-sub", &self.w_sub())
527 .field("h-sub", &self.h_sub())
528 .field("unpack-format", &self.unpack_format())
529 .field("pack-lines", &self.pack_lines())
530 .field("tile-mode", &self.tile_mode())
531 .field("tile-ws", &self.tile_ws())
532 .field("tile-hs", &self.tile_hs());
533
534 #[cfg(feature = "v1_22")]
535 {
536 fmt.field(
537 "tile-info",
538 &(0..self.n_planes()).map(|plane| self.tile_info(plane)),
539 );
540 }
541
542 fmt.finish()
543 }
544}
545
546impl fmt::Display for VideoFormatInfo {
547 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
548 f.write_str(self.name())
549 }
550}
551
552impl str::FromStr for crate::VideoFormatInfo {
553 type Err = glib::BoolError;
554
555 fn from_str(s: &str) -> Result<Self, Self::Err> {
556 skip_assert_initialized!();
557 let format = s.parse()?;
558 Ok(Self::from_format(format))
559 }
560}
561
562impl From<crate::VideoFormat> for VideoFormatInfo {
563 #[inline]
564 fn from(f: crate::VideoFormat) -> Self {
565 skip_assert_initialized!();
566 Self::from_format(f)
567 }
568}
569
570#[doc(hidden)]
571impl glib::translate::GlibPtrDefault for VideoFormatInfo {
572 type GlibType = *mut ffi::GstVideoFormatInfo;
573}
574
575#[doc(hidden)]
576unsafe impl glib::translate::TransparentPtrType for VideoFormatInfo {}
577
578#[doc(hidden)]
579impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstVideoFormatInfo> for VideoFormatInfo {
580 type Storage = PhantomData<&'a Self>;
581
582 #[inline]
583 fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstVideoFormatInfo, Self> {
584 glib::translate::Stash(self.0, PhantomData)
585 }
586
587 fn to_glib_full(&self) -> *const ffi::GstVideoFormatInfo {
588 unimplemented!()
589 }
590}
591
592#[doc(hidden)]
593impl glib::translate::FromGlibPtrNone<*mut ffi::GstVideoFormatInfo> for VideoFormatInfo {
594 #[inline]
595 unsafe fn from_glib_none(ptr: *mut ffi::GstVideoFormatInfo) -> Self {
596 Self(&*ptr)
597 }
598}
599
600#[doc(hidden)]
601impl glib::translate::FromGlibPtrNone<*const ffi::GstVideoFormatInfo> for VideoFormatInfo {
602 #[inline]
603 unsafe fn from_glib_none(ptr: *const ffi::GstVideoFormatInfo) -> Self {
604 Self(&*ptr)
605 }
606}
607
608#[cfg(feature = "v1_22")]
609#[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
610#[repr(transparent)]
611#[doc(alias = "GstVideoTileInfo")]
612pub struct VideoTileInfo(ffi::GstVideoTileInfo);
613
614#[cfg(feature = "v1_22")]
615#[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
616impl fmt::Debug for VideoTileInfo {
617 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
618 f.debug_struct("VideoTileInfo")
619 .field("width", &self.width())
620 .field("height", &self.height())
621 .field("stride", &self.stride())
622 .field("size", &self.size())
623 .finish()
624 }
625}
626
627#[cfg(feature = "v1_22")]
628#[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
629impl VideoTileInfo {
630 #[inline]
631 pub fn width(&self) -> u32 {
632 self.0.width
633 }
634
635 #[inline]
636 pub fn height(&self) -> u32 {
637 self.0.height
638 }
639
640 #[inline]
641 pub fn stride(&self) -> u32 {
642 self.0.stride
643 }
644
645 #[inline]
646 pub fn size(&self) -> u32 {
647 self.0.size
648 }
649}
650
651#[cfg(test)]
652mod tests {
653 use super::*;
654
655 #[test]
656 fn test_get() {
657 gst::init().unwrap();
658
659 let info = VideoFormatInfo::from_format(crate::VideoFormat::I420);
660 assert_eq!(info.name(), "I420");
661
662 let other_info = "I420".parse().unwrap();
663 assert_eq!(info, other_info);
664
665 assert_eq!(info.scale_width(0, 128), 128);
666 assert_eq!(info.scale_width(1, 128), 64);
667 assert_eq!(info.scale_width(2, 128), 64);
668 }
669
670 #[test]
671 fn test_unpack() {
672 gst::init().unwrap();
673
674 let input = &[&[0; 320][..], &[128; 160][..], &[128; 160][..]];
676 let intermediate = &mut [0; 320 * 4][..];
678 let output = &mut [&mut [0; 320][..], &mut [0; 160][..], &mut [0; 160][..]];
680
681 let info = VideoFormatInfo::from_format(crate::VideoFormat::I420);
682 assert_eq!(info.unpack_format(), crate::VideoFormat::Ayuv);
683 info.unpack(
684 crate::VideoPackFlags::empty(),
685 intermediate,
686 input,
687 &[320, 160, 160][..],
688 0,
689 0,
690 320,
691 );
692
693 for pixel in intermediate.chunks_exact(4) {
694 assert_eq!(&[255, 0, 128, 128][..], pixel);
695 }
696
697 info.pack(
698 crate::VideoPackFlags::empty(),
699 &intermediate[..(4 * 320)],
700 4 * 320,
701 output,
702 &[320, 160, 160][..],
703 crate::VideoChromaSite::NONE,
704 0,
705 320,
706 );
707 assert_eq!(input, output);
708 }
709}