diff --git a/examples/src/glupload.rs b/examples/src/glupload.rs index b6bec548f..5f779ce73 100644 --- a/examples/src/glupload.rs +++ b/examples/src/glupload.rs @@ -611,7 +611,7 @@ pub(crate) fn main_loop(app: App) -> Result<(), Error> { let gl = load(&app.windowed_context); - let mut curr_frame: Option> = None; + let mut curr_frame: Option> = None; let App { bus, @@ -651,7 +651,7 @@ pub(crate) fn main_loop(app: App) -> Result<(), Error> { glutin::event::Event::RedrawRequested(_) => needs_redraw = true, // Receive a frame glutin::event::Event::UserEvent(Message::Frame(info, buffer)) => { - if let Ok(frame) = gst_video::VideoFrame::from_buffer_readable_gl(buffer, &info) { + if let Ok(frame) = gst_gl::GLVideoFrame::from_buffer_readable(buffer, &info) { curr_frame = Some(frame); needs_redraw = true; } diff --git a/gstreamer-gl/src/gl_video_frame.rs b/gstreamer-gl/src/gl_video_frame.rs index 71f2d5676..282b49f5a 100644 --- a/gstreamer-gl/src/gl_video_frame.rs +++ b/gstreamer-gl/src/gl_video_frame.rs @@ -1,52 +1,117 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::mem; +use std::{marker::PhantomData, mem, ptr}; -use glib::translate::{from_glib, ToGlibPtr}; -use gst_video::video_frame::Readable; +use glib::translate::{from_glib, Borrowed, ToGlibPtr}; +use gst_video::VideoFrameExt; -pub trait VideoFrameGLExt { - fn from_buffer_readable_gl( - buffer: gst::Buffer, - info: &gst_video::VideoInfo, - ) -> Result, gst::Buffer>; +pub enum Readable {} - fn from_buffer_ref_readable_gl<'a>( - buffer: &'a gst::BufferRef, - info: &gst_video::VideoInfo, - ) -> Result, glib::error::BoolError>; +// TODO: implement copy for videoframes. This would need to go through all the individual +// memoryies and copy them. Some GL textures can be copied, others cannot. +pub struct GLVideoFrame { + frame: gst_video::ffi::GstVideoFrame, + buffer: gst::Buffer, + phantom: PhantomData, +} + +unsafe impl Send for GLVideoFrame {} +unsafe impl Sync for GLVideoFrame {} + +// TODO implement Debug for GLVideoFrame + +impl VideoFrameExt for GLVideoFrame { + #[inline] + fn info(&self) -> &gst_video::VideoInfo { + unsafe { + &*(&self.frame.info as *const gst_video::ffi::GstVideoInfo + as *const gst_video::VideoInfo) + } + } + + #[inline] + fn flags(&self) -> gst_video::VideoFrameFlags { + unsafe { from_glib(self.frame.flags) } + } + + #[inline] + fn id(&self) -> i32 { + self.frame.id + } +} + +impl GLVideoFrame { #[doc(alias = "get_texture_id")] - fn texture_id(&self, idx: u32) -> Option; -} - -impl VideoFrameGLExt for gst_video::VideoFrame { - fn from_buffer_readable_gl( - buffer: gst::Buffer, - info: &gst_video::VideoInfo, - ) -> Result, gst::Buffer> { - skip_assert_initialized!(); - gst_video::VideoFrameRef::<&gst::BufferRef>::from_buffer_readable_gl(buffer, info) + pub fn texture_id(&self, idx: u32) -> Option { + self.as_video_frame_gl_ref().texture_id(idx) } - fn from_buffer_ref_readable_gl<'a>( - buffer: &'a gst::BufferRef, - info: &gst_video::VideoInfo, - ) -> Result, glib::error::BoolError> { - skip_assert_initialized!(); - gst_video::VideoFrameRef::<&gst::BufferRef>::from_buffer_ref_readable_gl(buffer, info) + #[inline] + pub fn into_buffer(self) -> gst::Buffer { + unsafe { + let mut s = mem::ManuallyDrop::new(self); + let buffer = ptr::read(&s.buffer); + gst_video::ffi::gst_video_frame_unmap(&mut s.frame); + buffer + } } - fn texture_id(&self, idx: u32) -> Option { - self.as_video_frame_ref().texture_id(idx) + #[inline] + pub fn buffer(&self) -> &gst::BufferRef { + unsafe { gst::BufferRef::from_ptr(self.frame.buffer) } + } + + #[inline] + pub unsafe fn from_glib_full(frame: gst_video::ffi::GstVideoFrame) -> Self { + let buffer = gst::Buffer::from_glib_none(frame.buffer); + Self { + frame, + buffer, + phantom: PhantomData, + } + } + + #[inline] + pub fn as_ptr(&self) -> *const gst_video::ffi::GstVideoFrame { + &self.frame + } + + #[inline] + pub fn into_raw(self) -> gst_video::ffi::GstVideoFrame { + unsafe { + let mut s = mem::ManuallyDrop::new(self); + ptr::drop_in_place(&mut s.buffer); + s.frame + } + } + + #[inline] + pub fn as_video_frame_gl_ref(&self) -> GLVideoFrameRef<&gst::BufferRef> { + let frame = unsafe { ptr::read(&self.frame) }; + GLVideoFrameRef { + frame, + unmap: false, + phantom: PhantomData, + } } } -impl<'a> VideoFrameGLExt for gst_video::VideoFrameRef<&'a gst::BufferRef> { - fn from_buffer_readable_gl( +impl Drop for GLVideoFrame { + #[inline] + fn drop(&mut self) { + unsafe { + gst_video::ffi::gst_video_frame_unmap(&mut self.frame); + } + } +} + +impl GLVideoFrame { + #[inline] + pub fn from_buffer_readable( buffer: gst::Buffer, info: &gst_video::VideoInfo, - ) -> Result, gst::Buffer> { + ) -> Result { skip_assert_initialized!(); let n_mem = match buffer_n_gl_memory(buffer.as_ref()) { @@ -82,15 +147,80 @@ impl<'a> VideoFrameGLExt for gst_video::VideoFrameRef<&'a gst::BufferRef> { frame.info.size = 0; frame.info.stride.fill(0); frame.info.offset.fill(0); - Ok(gst_video::VideoFrame::from_glib_full(frame)) + Ok(Self { + frame, + buffer, + phantom: PhantomData, + }) } } } +} - fn from_buffer_ref_readable_gl<'b>( - buffer: &'b gst::BufferRef, - info: &gst_video::VideoInfo, - ) -> Result, glib::error::BoolError> { +pub struct GLVideoFrameRef { + frame: gst_video::ffi::GstVideoFrame, + unmap: bool, + phantom: PhantomData, +} + +unsafe impl Send for GLVideoFrameRef {} +unsafe impl Sync for GLVideoFrameRef {} + +impl VideoFrameExt for GLVideoFrameRef { + #[inline] + fn info(&self) -> &gst_video::VideoInfo { + unsafe { + &*(&self.frame.info as *const gst_video::ffi::GstVideoInfo + as *const gst_video::VideoInfo) + } + } + + #[inline] + fn flags(&self) -> gst_video::VideoFrameFlags { + unsafe { from_glib(self.frame.flags) } + } + + #[inline] + fn id(&self) -> i32 { + self.frame.id + } +} +// TODO implement Debug for GLVideoFrameRef +// +impl GLVideoFrameRef { + #[inline] + pub fn as_ptr(&self) -> *const gst_video::ffi::GstVideoFrame { + &self.frame + } +} + +impl<'a> GLVideoFrameRef<&'a gst::BufferRef> { + #[inline] + pub unsafe fn from_glib_borrow(frame: *const gst_video::ffi::GstVideoFrame) -> Borrowed { + debug_assert!(!frame.is_null()); + + let frame = ptr::read(frame); + Borrowed::new(Self { + frame, + unmap: false, + phantom: PhantomData, + }) + } + + #[inline] + pub unsafe fn from_glib_full(frame: gst_video::ffi::GstVideoFrame) -> Self { + Self { + frame, + unmap: true, + phantom: PhantomData, + } + } + + #[inline] + pub fn from_buffer_ref_readable<'b>( + buffer: &'a gst::BufferRef, + info: &'b gst_video::VideoInfo, + ) -> Result, glib::error::BoolError> { skip_assert_initialized!(); let n_mem = match buffer_n_gl_memory(buffer) { @@ -130,12 +260,20 @@ impl<'a> VideoFrameGLExt for gst_video::VideoFrameRef<&'a gst::BufferRef> { frame.info.size = 0; frame.info.stride.fill(0); frame.info.offset.fill(0); - Ok(gst_video::VideoFrameRef::from_glib_full(frame)) + Ok(Self { + frame, + unmap: true, + phantom: PhantomData, + }) } } } - fn texture_id(&self, idx: u32) -> Option { + pub fn buffer(&self) -> &gst::BufferRef { + unsafe { gst::BufferRef::from_ptr(self.frame.buffer) } + } + + pub fn texture_id(&self, idx: u32) -> Option { let len = buffer_n_gl_memory(self.buffer())?; if idx >= len { @@ -154,6 +292,17 @@ impl<'a> VideoFrameGLExt for gst_video::VideoFrameRef<&'a gst::BufferRef> { } } +impl Drop for GLVideoFrameRef { + #[inline] + fn drop(&mut self) { + unsafe { + if self.unmap { + gst_video::ffi::gst_video_frame_unmap(&mut self.frame); + } + } + } +} + fn buffer_n_gl_memory(buffer: &gst::BufferRef) -> Option { skip_assert_initialized!(); unsafe { diff --git a/gstreamer-gl/src/lib.rs b/gstreamer-gl/src/lib.rs index 5d1ad6f63..d14c527a2 100644 --- a/gstreamer-gl/src/lib.rs +++ b/gstreamer-gl/src/lib.rs @@ -36,8 +36,9 @@ pub use crate::functions::*; mod gl_context; mod gl_display; mod gl_sync_meta; -mod gl_video_frame; +pub mod gl_video_frame; pub use crate::gl_sync_meta::*; +pub use crate::gl_video_frame::{GLVideoFrame, GLVideoFrameRef, Readable}; mod gl_base_memory; pub use self::gl_base_memory::*; mod gl_memory; @@ -55,7 +56,6 @@ pub mod prelude { pub use crate::{ auto::traits::*, context::ContextGLExt, gl_context::GLContextExtManual, gl_display::GLDisplayExtManual, gl_framebuffer::GLFramebufferExtManual, - gl_video_frame::VideoFrameGLExt, }; }