Skip to main content

gstreamer_video/
video_format_info.rs

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