diff --git a/Gir_GstVideo.toml b/Gir_GstVideo.toml index 94018f023..d2bbdb507 100644 --- a/Gir_GstVideo.toml +++ b/Gir_GstVideo.toml @@ -38,6 +38,7 @@ generate = [ "GstVideo.VideoTimeCodeFlags", "GstVideo.VideoCaptionType", "GstVideo.VideoBufferPool", + "GstVideo.VideoPackFlags", ] manual = [ diff --git a/gstreamer-video/src/auto/flags.rs b/gstreamer-video/src/auto/flags.rs index 7c2f07904..20da65661 100644 --- a/gstreamer-video/src/auto/flags.rs +++ b/gstreamer-video/src/auto/flags.rs @@ -355,6 +355,55 @@ impl SetValue for VideoOverlayFormatFlags { } } +bitflags! { + pub struct VideoPackFlags: u32 { + const NONE = 0; + const TRUNCATE_RANGE = 1; + const INTERLACED = 2; + } +} + +#[doc(hidden)] +impl ToGlib for VideoPackFlags { + type GlibType = gst_video_sys::GstVideoPackFlags; + + fn to_glib(&self) -> gst_video_sys::GstVideoPackFlags { + self.bits() + } +} + +#[doc(hidden)] +impl FromGlib for VideoPackFlags { + fn from_glib(value: gst_video_sys::GstVideoPackFlags) -> VideoPackFlags { + skip_assert_initialized!(); + VideoPackFlags::from_bits_truncate(value) + } +} + +impl StaticType for VideoPackFlags { + fn static_type() -> Type { + unsafe { from_glib(gst_video_sys::gst_video_pack_flags_get_type()) } + } +} + +impl<'a> FromValueOptional<'a> for VideoPackFlags { + unsafe fn from_value_optional(value: &Value) -> Option { + Some(FromValue::from_value(value)) + } +} + +impl<'a> FromValue<'a> for VideoPackFlags { + unsafe fn from_value(value: &Value) -> Self { + from_glib(gobject_sys::g_value_get_flags(value.to_glib_none().0)) + } +} + +impl SetValue for VideoPackFlags { + unsafe fn set_value(value: &mut Value, this: &Self) { + gobject_sys::g_value_set_flags(value.to_glib_none_mut().0, this.to_glib()) + } +} + #[cfg(any(feature = "v1_10", feature = "dox"))] bitflags! { pub struct VideoTimeCodeFlags: u32 { diff --git a/gstreamer-video/src/auto/mod.rs b/gstreamer-video/src/auto/mod.rs index 5e6b487fc..4defd9e46 100644 --- a/gstreamer-video/src/auto/mod.rs +++ b/gstreamer-video/src/auto/mod.rs @@ -42,6 +42,7 @@ pub use self::flags::VideoFormatFlags; pub use self::flags::VideoFrameFlags; pub use self::flags::VideoMultiviewFlags; pub use self::flags::VideoOverlayFormatFlags; +pub use self::flags::VideoPackFlags; #[cfg(any(feature = "v1_10", feature = "dox"))] pub use self::flags::VideoTimeCodeFlags; diff --git a/gstreamer-video/src/video_format_info.rs b/gstreamer-video/src/video_format_info.rs index 0b6629bf1..22589eae4 100644 --- a/gstreamer-video/src/video_format_info.rs +++ b/gstreamer-video/src/video_format_info.rs @@ -145,7 +145,157 @@ impl VideoFormatInfo { (-((-(i64::from(height))) >> self.h_sub()[component as usize])) as u32 } - // TODO: pack/unpack + pub fn unpack( + &self, + flags: ::VideoPackFlags, + dest: &mut [u8], + src: &[&[u8]], + stride: &[i32], + x: i32, + y: i32, + width: i32, + ) { + let unpack_format = Self::from_format(self.unpack_format()); + + if unpack_format.pixel_stride()[0] == 0 || self.0.unpack_func.is_none() { + panic!("No unpack format for {:?}", self); + } + + if src.len() != self.n_planes() as usize { + panic!( + "Wrong number of planes provided for format: {} != {}", + src.len(), + self.n_planes() + ); + } + + if stride.len() != self.n_planes() as usize { + panic!( + "Wrong number of strides provided for format: {} != {}", + stride.len(), + self.n_planes() + ); + } + + if dest.len() < unpack_format.pixel_stride()[0] as usize * width as usize { + panic!("Too small destination slice"); + } + + for plane in 0..(self.n_planes()) { + if stride[plane as usize] + < self.scale_width(plane as u8, width as u32) as i32 + * self.pixel_stride()[plane as usize] + { + panic!("Too small source stride for plane {}", plane); + } + + let plane_size = y * stride[plane as usize] + + self.scale_width(plane as u8, (x + width) as u32) as i32 + * self.pixel_stride()[plane as usize]; + + if src[plane as usize].len() < plane_size as usize { + panic!("Too small source plane size for plane {}", plane); + } + } + + unsafe { + use std::ptr; + + let mut src_ptr = + [ptr::null() as *const u8; gst_video_sys::GST_VIDEO_MAX_PLANES as usize]; + for plane in 0..(self.n_planes()) { + src_ptr[plane as usize] = src[plane as usize].as_ptr(); + } + + (self.0.unpack_func.as_ref().unwrap())( + self.0, + flags.to_glib(), + dest.as_mut_ptr() as *mut _, + src_ptr.as_ptr() as *const _, + stride.as_ptr() as *const i32, + x, + y, + width, + ); + } + } + + pub fn pack( + &self, + flags: ::VideoPackFlags, + src: &[u8], + src_stride: i32, + dest: &mut [&mut [u8]], + dest_stride: &[i32], + chroma_site: ::VideoChromaSite, + y: i32, + width: i32, + ) { + let unpack_format = Self::from_format(self.unpack_format()); + + if unpack_format.pixel_stride()[0] == 0 || self.0.unpack_func.is_none() { + panic!("No unpack format for {:?}", self); + } + + if dest.len() != self.n_planes() as usize { + panic!( + "Wrong number of planes provided for format: {} != {}", + dest.len(), + self.n_planes() + ); + } + + if dest_stride.len() != self.n_planes() as usize { + panic!( + "Wrong number of strides provided for format: {} != {}", + dest_stride.len(), + self.n_planes() + ); + } + + if src.len() < unpack_format.pixel_stride()[0] as usize * width as usize { + panic!("Too small source slice"); + } + + for plane in 0..(self.n_planes()) { + if dest_stride[plane as usize] + < self.scale_width(plane as u8, width as u32) as i32 + * self.pixel_stride()[plane as usize] + { + panic!("Too small destination stride for plane {}", plane); + } + + let plane_size = y * dest_stride[plane as usize] + + self.scale_width(plane as u8, width as u32) as i32 + * self.pixel_stride()[plane as usize]; + + if dest[plane as usize].len() < plane_size as usize { + panic!("Too small destination plane size for plane {}", plane); + } + } + + unsafe { + use std::ptr; + + let mut dest_ptr = + [ptr::null_mut() as *mut u8; gst_video_sys::GST_VIDEO_MAX_PLANES as usize]; + for plane in 0..(self.n_planes()) { + dest_ptr[plane as usize] = dest[plane as usize].as_mut_ptr(); + } + + (self.0.pack_func.as_ref().unwrap())( + self.0, + flags.to_glib(), + src.as_ptr() as *mut _, + src_stride, + dest_ptr.as_mut_ptr() as *mut _, + dest_stride.as_ptr() as *const i32, + chroma_site.to_glib(), + y, + width, + ); + } + } } unsafe impl Sync for VideoFormatInfo {} @@ -237,4 +387,44 @@ mod tests { assert_eq!(info.scale_width(1, 128), 64); assert_eq!(info.scale_width(2, 128), 64); } + + #[test] + fn test_unpack() { + gst::init().unwrap(); + + // One line black 320 pixel I420 + let input = &[&[0; 320][..], &[128; 160][..], &[128; 160][..]]; + // One line of AYUV + let intermediate = &mut [0; 320 * 4][..]; + // One line of 320 pixel I420 + let output = &mut [&mut [0; 320][..], &mut [0; 160][..], &mut [0; 160][..]]; + + let info = VideoFormatInfo::from_format(::VideoFormat::I420); + assert_eq!(info.unpack_format(), ::VideoFormat::Ayuv); + info.unpack( + ::VideoPackFlags::empty(), + intermediate, + input, + &[320, 160, 160][..], + 0, + 0, + 320, + ); + + for pixel in intermediate.chunks_exact(4) { + assert_eq!(&[255, 0, 128, 128][..], pixel); + } + + info.pack( + ::VideoPackFlags::empty(), + &intermediate[..(4 * 320)], + 4 * 320, + output, + &[320, 160, 160][..], + ::VideoChromaSite::NONE, + 0, + 320, + ); + assert_eq!(input, output); + } }