// 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; use std::ops; 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 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> { skip_assert_initialized!(); 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(::VideoFrameFlags::INTERLACED) } pub fn is_tff(&self) -> bool { self.flags().contains(::VideoFrameFlags::TFF) } pub fn is_rff(&self) -> bool { self.flags().contains(::VideoFrameFlags::RFF) } pub fn is_onefield(&self) -> bool { self.flags().contains(::VideoFrameFlags::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() } 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 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> { skip_assert_initialized!(); 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> { skip_assert_initialized!(); 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 as_video_frame_ref(&self) -> VideoFrameRef<&gst::BufferRef> { let vframe = unsafe { ptr::read(&self.0) }; let info = self.2.clone(); VideoFrameRef(vframe, Some(self.buffer()), info, true) } } impl VideoFrame { pub fn from_buffer_writable( buffer: gst::Buffer, info: &::VideoInfo, ) -> Result, gst::Buffer> { skip_assert_initialized!(); 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> { skip_assert_initialized!(); 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(&mut self) -> &mut gst::BufferRef { unsafe { gst::BufferRef::from_mut_ptr(self.0.buffer) } } pub fn plane_data_mut(&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, )) } } pub fn as_mut_video_frame_ref(&mut self) -> VideoFrameRef<&mut gst::BufferRef> { let vframe = unsafe { ptr::read(&self.0) }; let info = self.2.clone(); VideoFrameRef(vframe, Some(self.buffer_mut()), info, true) } } pub struct VideoFrameRef(ffi::GstVideoFrame, Option, ::VideoInfo, bool); impl<'a> VideoFrameRef<&'a gst::BufferRef> { pub fn from_buffer_ref_readable<'b>( buffer: &'a gst::BufferRef, info: &'b ::VideoInfo, ) -> Option> { skip_assert_initialized!(); 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.as_mut_ptr(), mem::transmute( ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF.bits() | gst_ffi::GST_MAP_READ.bits(), ), )); if !res { None } else { let info = ::VideoInfo(ptr::read(&frame.info)); Some(VideoFrameRef(frame, Some(buffer), info, false)) } } } pub fn from_buffer_ref_id_readable<'b>( buffer: &'a gst::BufferRef, id: i32, info: &'b ::VideoInfo, ) -> Option> { skip_assert_initialized!(); 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.as_mut_ptr(), id, mem::transmute( ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF.bits() | gst_ffi::GST_MAP_READ.bits(), ), )); if !res { None } else { let info = ::VideoInfo(ptr::read(&frame.info)); Some(VideoFrameRef(frame, Some(buffer), info, false)) } } } pub fn info(&self) -> &::VideoInfo { &self.2 } pub fn flags(&self) -> ::VideoFrameFlags { from_glib(self.0.flags) } pub fn id(&self) -> i32 { self.0.id } pub fn copy(&self, dest: &mut VideoFrameRef<&mut gst::BufferRef>) -> 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 VideoFrameRef<&mut gst::BufferRef>, plane: u32, ) -> Result<(), glib::BoolError> { skip_assert_initialized!(); 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(::VideoFrameFlags::INTERLACED) } pub fn is_tff(&self) -> bool { self.flags().contains(::VideoFrameFlags::TFF) } pub fn is_rff(&self) -> bool { self.flags().contains(::VideoFrameFlags::RFF) } pub fn is_onefield(&self) -> bool { self.flags().contains(::VideoFrameFlags::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() } pub fn buffer(&self) -> &gst::BufferRef { self.1.as_ref().unwrap() } 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<'a> VideoFrameRef<&'a mut gst::BufferRef> { pub fn from_buffer_ref_writable<'b>( buffer: &'a mut gst::BufferRef, info: &'b ::VideoInfo, ) -> Option> { skip_assert_initialized!(); 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.as_mut_ptr(), 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 { None } else { let info = ::VideoInfo(ptr::read(&frame.info)); Some(VideoFrameRef(frame, Some(buffer), info, false)) } } } pub fn from_buffer_ref_id_writable<'b>( buffer: &'a mut gst::BufferRef, id: i32, info: &'b ::VideoInfo, ) -> Option> { skip_assert_initialized!(); 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.as_mut_ptr(), 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 { None } else { let info = ::VideoInfo(ptr::read(&frame.info)); Some(VideoFrameRef(frame, Some(buffer), info, false)) } } } pub fn buffer_mut(&mut self) -> &mut gst::BufferRef { self.1.as_mut().unwrap() } pub fn plane_data_mut(&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, )) } } } impl<'a> ops::Deref for VideoFrameRef<&'a mut gst::BufferRef> { type Target = VideoFrameRef<&'a gst::BufferRef>; fn deref(&self) -> &Self::Target { use std::mem; unsafe { mem::transmute(self) } } } impl Drop for VideoFrameRef { fn drop(&mut self) { if !self.3 { unsafe { ffi::gst_video_frame_unmap(&mut self.0); } } } } #[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::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::with_size(info.size()).unwrap(); let mut frame = VideoFrame::from_buffer_writable(buffer, &info).unwrap(); assert_ne!(frame.plane_data_mut(0), None); assert_eq!(frame.plane_data_mut(0).unwrap().len(), 320 * 240); assert_eq!(frame.plane_data_mut(1), None); assert!(frame.info() == &info); } }