From cb8ca2c00e40ec8016ba72976b3d159ad9ba9ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 11 Aug 2017 14:55:31 +0300 Subject: [PATCH] Add VideoFrame bindings --- Gir_GstVideo.toml | 2 + gstreamer-video/src/auto/flags.rs | 53 +++++ gstreamer-video/src/auto/mod.rs | 8 + gstreamer-video/src/lib.rs | 2 + gstreamer-video/src/video_frame.rs | 357 +++++++++++++++++++++++++++++ gstreamer-video/src/video_info.rs | 57 ++++- 6 files changed, 478 insertions(+), 1 deletion(-) create mode 100644 gstreamer-video/src/video_frame.rs diff --git a/Gir_GstVideo.toml b/Gir_GstVideo.toml index d00b9c76d..0de6e23b9 100644 --- a/Gir_GstVideo.toml +++ b/Gir_GstVideo.toml @@ -27,6 +27,7 @@ generate = [ "GstVideo.VideoMultiviewMode", "GstVideo.VideoMultiviewFlags", "GstVideo.VideoFieldOrder", + "GstVideo.VideoFrameFlags", ] manual = [ @@ -36,6 +37,7 @@ manual = [ "GstVideo.VideoFormatInfo", "GstVideo.VideoColorimetry", "GstVideo.VideoColorRange", + "GstVideo.VideoFrame", ] [[object]] diff --git a/gstreamer-video/src/auto/flags.rs b/gstreamer-video/src/auto/flags.rs index 9110d77e4..1304c3928 100644 --- a/gstreamer-video/src/auto/flags.rs +++ b/gstreamer-video/src/auto/flags.rs @@ -167,6 +167,59 @@ impl SetValue for VideoFormatFlags { } } +bitflags! { + pub struct VideoFrameFlags: u32 { + const VIDEO_FRAME_FLAG_NONE = 0; + const VIDEO_FRAME_FLAG_INTERLACED = 1; + const VIDEO_FRAME_FLAG_TFF = 2; + const VIDEO_FRAME_FLAG_RFF = 4; + const VIDEO_FRAME_FLAG_ONEFIELD = 8; + const VIDEO_FRAME_FLAG_MULTIPLE_VIEW = 16; + const VIDEO_FRAME_FLAG_FIRST_IN_BUNDLE = 32; + } +} + +#[doc(hidden)] +impl ToGlib for VideoFrameFlags { + type GlibType = ffi::GstVideoFrameFlags; + + fn to_glib(&self) -> ffi::GstVideoFrameFlags { + ffi::GstVideoFrameFlags::from_bits_truncate(self.bits()) + } +} + +#[doc(hidden)] +impl FromGlib for VideoFrameFlags { + fn from_glib(value: ffi::GstVideoFrameFlags) -> VideoFrameFlags { + skip_assert_initialized!(); + VideoFrameFlags::from_bits_truncate(value.bits()) + } +} + +impl StaticType for VideoFrameFlags { + fn static_type() -> Type { + unsafe { from_glib(ffi::gst_video_frame_flags_get_type()) } + } +} + +impl<'a> FromValueOptional<'a> for VideoFrameFlags { + unsafe fn from_value_optional(value: &Value) -> Option { + Some(FromValue::from_value(value)) + } +} + +impl<'a> FromValue<'a> for VideoFrameFlags { + unsafe fn from_value(value: &Value) -> Self { + from_glib(ffi::GstVideoFrameFlags::from_bits_truncate(gobject_ffi::g_value_get_flags(value.to_glib_none().0))) + } +} + +impl SetValue for VideoFrameFlags { + unsafe fn set_value(value: &mut Value, this: &Self) { + gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, this.to_glib().bits()) + } +} + bitflags! { pub struct VideoMultiviewFlags: u32 { const VIDEO_MULTIVIEW_FLAGS_NONE = 0; diff --git a/gstreamer-video/src/auto/mod.rs b/gstreamer-video/src/auto/mod.rs index 9d1715a6d..2800773a3 100644 --- a/gstreamer-video/src/auto/mod.rs +++ b/gstreamer-video/src/auto/mod.rs @@ -37,6 +37,14 @@ pub use self::flags::VIDEO_FORMAT_FLAG_PALETTE; pub use self::flags::VIDEO_FORMAT_FLAG_COMPLEX; pub use self::flags::VIDEO_FORMAT_FLAG_UNPACK; pub use self::flags::VIDEO_FORMAT_FLAG_TILED; +pub use self::flags::VideoFrameFlags; +pub use self::flags::VIDEO_FRAME_FLAG_NONE; +pub use self::flags::VIDEO_FRAME_FLAG_INTERLACED; +pub use self::flags::VIDEO_FRAME_FLAG_TFF; +pub use self::flags::VIDEO_FRAME_FLAG_RFF; +pub use self::flags::VIDEO_FRAME_FLAG_ONEFIELD; +pub use self::flags::VIDEO_FRAME_FLAG_MULTIPLE_VIEW; +pub use self::flags::VIDEO_FRAME_FLAG_FIRST_IN_BUNDLE; pub use self::flags::VideoMultiviewFlags; pub use self::flags::VIDEO_MULTIVIEW_FLAGS_NONE; pub use self::flags::VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST; diff --git a/gstreamer-video/src/lib.rs b/gstreamer-video/src/lib.rs index 4af20ec9c..8c7d86681 100644 --- a/gstreamer-video/src/lib.rs +++ b/gstreamer-video/src/lib.rs @@ -38,3 +38,5 @@ mod video_format_info; pub use video_format_info::*; mod video_info; pub use video_info::*; +mod video_frame; +pub use video_frame::VideoFrame; diff --git a/gstreamer-video/src/video_frame.rs b/gstreamer-video/src/video_frame.rs new file mode 100644 index 000000000..4d2a9ca04 --- /dev/null +++ b/gstreamer-video/src/video_frame.rs @@ -0,0 +1,357 @@ +// Copyright (C) 2017 Sebastian Dröge +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi; +use gst_ffi; + +use gst; +use gst::miniobject::MiniObject; +use glib; +use glib::translate::{from_glib, ToGlibPtr}; + +use std::mem; +use std::ptr; +use std::marker::PhantomData; +use std::slice; + +pub struct Readable; +pub struct Writable; +pub struct VideoFrame( + ffi::GstVideoFrame, + Option, + ::VideoInfo, + PhantomData, +); + +impl VideoFrame { + pub fn info(&self) -> &::VideoInfo { + &self.2 + } + + pub fn flags(&self) -> ::VideoFrameFlags { + from_glib(self.0.flags) + } + + pub fn mut_buffer(&self) -> Option<&mut gst::BufferRef> { + unsafe { + let writable: bool = from_glib(gst_ffi::gst_mini_object_is_writable( + self.0.buffer as *const _, + )); + if !writable { + return None; + } + + Some(gst::BufferRef::from_mut_ptr(self.0.buffer)) + } + } + + pub fn id(&self) -> i32 { + self.0.id + } + + pub fn into_buffer(mut self) -> gst::Buffer { + self.1.take().unwrap() + } + + pub fn copy(&self, dest: &mut VideoFrame) -> Result<(), glib::BoolError> { + unsafe { + let res: bool = from_glib(ffi::gst_video_frame_copy(&mut dest.0, &self.0)); + if res { + Ok(()) + } else { + Err(glib::BoolError("Failed to copy video frame")) + } + } + } + + pub fn copy_plane( + &self, + dest: &mut VideoFrame, + plane: u32, + ) -> Result<(), glib::BoolError> { + unsafe { + let res: bool = from_glib(ffi::gst_video_frame_copy_plane(&mut dest.0, &self.0, plane)); + if res { + Ok(()) + } else { + Err(glib::BoolError("Failed to copy video frame plane")) + } + } + } + + pub fn format(&self) -> ::VideoFormat { + self.info().format() + } + + pub fn format_info(&self) -> ::VideoFormatInfo { + self.info().format_info() + } + + pub fn width(&self) -> u32 { + self.info().width() + } + + pub fn height(&self) -> u32 { + self.info().height() + } + + pub fn size(&self) -> usize { + self.info().size() + } + + pub fn is_interlaced(&self) -> bool { + self.flags().contains(::VIDEO_FRAME_FLAG_INTERLACED) + } + + pub fn is_tff(&self) -> bool { + self.flags().contains(::VIDEO_FRAME_FLAG_TFF) + } + + pub fn is_rff(&self) -> bool { + self.flags().contains(::VIDEO_FRAME_FLAG_RFF) + } + + pub fn is_onefield(&self) -> bool { + self.flags().contains(::VIDEO_FRAME_FLAG_ONEFIELD) + } + + pub fn n_planes(&self) -> u32 { + self.info().n_planes() + } + + pub fn n_components(&self) -> u32 { + self.info().n_components() + } + + pub fn plane_stride(&self) -> &[i32] { + self.info().stride() + } + + pub fn plane_offset(&self) -> &[usize] { + self.info().offset() + } +} + +impl Drop for VideoFrame { + fn drop(&mut self) { + unsafe { + ffi::gst_video_frame_unmap(&mut self.0); + } + } +} + +impl VideoFrame { + pub fn from_buffer_readable( + buffer: gst::Buffer, + info: &::VideoInfo, + ) -> Result, gst::Buffer> { + unsafe { + let mut frame = mem::zeroed(); + let res: bool = from_glib(ffi::gst_video_frame_map( + &mut frame, + info.to_glib_none().0 as *mut _, + buffer.to_glib_none().0, + mem::transmute( + ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF.bits() | gst_ffi::GST_MAP_READ.bits(), + ), + )); + + if !res { + Err(buffer) + } else { + let info = ::VideoInfo(ptr::read(&frame.info)); + Ok(VideoFrame(frame, Some(buffer), info, PhantomData)) + } + } + } + + pub fn from_buffer_id_readable( + buffer: gst::Buffer, + id: i32, + info: &::VideoInfo, + ) -> Result, gst::Buffer> { + unsafe { + let mut frame = mem::zeroed(); + let res: bool = from_glib(ffi::gst_video_frame_map_id( + &mut frame, + info.to_glib_none().0 as *mut _, + buffer.to_glib_none().0, + id, + mem::transmute( + ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF.bits() | gst_ffi::GST_MAP_READ.bits(), + ), + )); + + if !res { + Err(buffer) + } else { + let info = ::VideoInfo(ptr::read(&frame.info)); + Ok(VideoFrame(frame, Some(buffer), info, PhantomData)) + } + } + } + + pub fn buffer(&self) -> &gst::BufferRef { + unsafe { gst::BufferRef::from_ptr(self.0.buffer) } + } + + + pub fn plane_data(&self, plane: u32) -> Option<&[u8]> { + if plane >= self.n_planes() { + return None; + } + + let format_info = self.format_info(); + + // Just get the palette + if format_info.has_palette() && plane == 1 { + unsafe { + return Some(slice::from_raw_parts(self.0.data[1] as *const u8, 256 * 4)); + } + } + + let w = self.plane_stride()[plane as usize] as u32; + // FIXME: This assumes that the horizontal subsampling of all + // components in the plane is the same, which is probably safe + let h = format_info.scale_height(plane as u8, self.height()); + + unsafe { + Some(slice::from_raw_parts( + self.0.data[plane as usize] as *const u8, + (w * h) as usize, + )) + } + } +} + +impl VideoFrame { + pub fn from_buffer_writable( + buffer: gst::Buffer, + info: &::VideoInfo, + ) -> Result, gst::Buffer> { + unsafe { + let mut frame = mem::zeroed(); + let res: bool = from_glib(ffi::gst_video_frame_map( + &mut frame, + info.to_glib_none().0 as *mut _, + buffer.to_glib_none().0, + mem::transmute( + ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF.bits() | gst_ffi::GST_MAP_READ.bits() | + gst_ffi::GST_MAP_WRITE.bits(), + ), + )); + + if !res { + Err(buffer) + } else { + let info = ::VideoInfo(ptr::read(&frame.info)); + Ok(VideoFrame(frame, Some(buffer), info, PhantomData)) + } + } + } + + pub fn from_buffer_id_writable( + buffer: gst::Buffer, + id: i32, + info: &::VideoInfo, + ) -> Result, gst::Buffer> { + unsafe { + let mut frame = mem::zeroed(); + let res: bool = from_glib(ffi::gst_video_frame_map_id( + &mut frame, + info.to_glib_none().0 as *mut _, + buffer.to_glib_none().0, + id, + mem::transmute( + ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF.bits() | gst_ffi::GST_MAP_READ.bits() | + gst_ffi::GST_MAP_WRITE.bits(), + ), + )); + + if !res { + Err(buffer) + } else { + let info = ::VideoInfo(ptr::read(&frame.info)); + Ok(VideoFrame(frame, Some(buffer), info, PhantomData)) + } + } + } + + pub fn buffer(&mut self) -> &mut gst::BufferRef { + unsafe { gst::BufferRef::from_mut_ptr(self.0.buffer) } + } + + pub fn plane_data(&mut self, plane: u32) -> Option<&mut [u8]> { + if plane >= self.n_planes() { + return None; + } + + let format_info = self.format_info(); + + // Just get the palette + if format_info.has_palette() && plane == 1 { + unsafe { + return Some(slice::from_raw_parts_mut( + self.0.data[1] as *mut u8, + 256 * 4, + )); + } + } + + let w = self.plane_stride()[plane as usize] as u32; + // FIXME: This assumes that the horizontal subsampling of all + // components in the plane is the same, which is probably safe + let h = format_info.scale_height(plane as u8, self.height()); + + unsafe { + Some(slice::from_raw_parts_mut( + self.0.data[plane as usize] as *mut u8, + (w * h) as usize, + )) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use gst; + + #[test] + fn test_map_read() { + gst::init().unwrap(); + + let info = ::VideoInfo::new(::VideoFormat::Gray8, 320, 240) + .build() + .unwrap(); + let buffer = gst::Buffer::new_with_size(info.size()).unwrap(); + let frame = VideoFrame::from_buffer_readable(buffer, &info).unwrap(); + + assert_ne!(frame.plane_data(0), None); + assert_eq!(frame.plane_data(0).unwrap().len(), 320 * 240); + assert_eq!(frame.plane_data(1), None); + assert!(frame.info() == &info); + } + + + #[test] + fn test_map_write() { + gst::init().unwrap(); + + let info = ::VideoInfo::new(::VideoFormat::Gray8, 320, 240) + .build() + .unwrap(); + let buffer = gst::Buffer::new_with_size(info.size()).unwrap(); + let mut frame = VideoFrame::from_buffer_writable(buffer, &info).unwrap(); + + assert_ne!(frame.plane_data(0), None); + assert_eq!(frame.plane_data(0).unwrap().len(), 320 * 240); + assert_eq!(frame.plane_data(1), None); + assert!(frame.info() == &info); + } +} diff --git a/gstreamer-video/src/video_info.rs b/gstreamer-video/src/video_info.rs index 99ba069fb..b4813fa5d 100644 --- a/gstreamer-video/src/video_info.rs +++ b/gstreamer-video/src/video_info.rs @@ -161,7 +161,7 @@ impl fmt::Display for ::VideoColorimetry { } } -pub struct VideoInfo(ffi::GstVideoInfo); +pub struct VideoInfo(pub(crate) ffi::GstVideoInfo); pub struct VideoInfoBuilder<'a> { format: ::VideoFormat, @@ -658,6 +658,61 @@ impl glib::translate::FromGlibPtrFull<*mut ffi::GstVideoInfo> for VideoInfo { } } +#[cfg(feature = "v1_12")] +impl ::VideoFieldOrder { + pub fn to_string(&self) -> String { + unsafe { from_glib_full(ffi::gst_video_field_order_to_string(self.to_glib())) } + } + + pub fn from_string(s: &str) -> Option { + unsafe { from_glib(ffi::gst_video_field_order_from_string(s.to_glib_none().0)) } + } +} + +#[cfg(feature = "v1_12")] +impl str::FromStr for ::VideoFieldOrder { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::from_string(s).ok_or(()) + } +} + +#[cfg(feature = "v1_12")] +impl fmt::Display for ::VideoFieldOrder { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.write_str(&self.to_string()) + } +} + +impl ::VideoInterlaceMode { + pub fn to_string(&self) -> String { + unsafe { from_glib_full(ffi::gst_video_interlace_mode_to_string(self.to_glib())) } + } + + pub fn from_string(s: &str) -> Self { + unsafe { + from_glib(ffi::gst_video_interlace_mode_from_string( + s.to_glib_none().0, + )) + } + } +} + +impl str::FromStr for ::VideoInterlaceMode { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(Self::from_string(s)) + } +} + +impl fmt::Display for ::VideoInterlaceMode { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.write_str(&self.to_string()) + } +} + #[cfg(test)] mod tests { use super::*;