diff --git a/gstreamer/src/buffer.rs b/gstreamer/src/buffer.rs index c9f14bdee..e78e58a14 100644 --- a/gstreamer/src/buffer.rs +++ b/gstreamer/src/buffer.rs @@ -33,6 +33,7 @@ pub struct MappedBuffer { impl Buffer { #[doc(alias = "gst_buffer_new")] + #[inline] pub fn new() -> Self { assert_initialized_main_thread!(); @@ -41,6 +42,7 @@ impl Buffer { #[doc(alias = "gst_buffer_new_allocate")] #[doc(alias = "gst_buffer_new_and_alloc")] + #[inline] pub fn with_size(size: usize) -> Result { assert_initialized_main_thread!(); @@ -54,55 +56,38 @@ impl Buffer { } } - unsafe extern "C" fn drop_box(vec: glib::ffi::gpointer) { - let slice: Box = Box::from_raw(vec as *mut T); - drop(slice); - } - + #[doc(alias = "gst_buffer_new_wrapped")] #[doc(alias = "gst_buffer_new_wrapped_full")] + #[inline] pub fn from_mut_slice + Send + 'static>(slice: T) -> Self { assert_initialized_main_thread!(); - unsafe { - let mut b = Box::new(slice); - let (size, data) = { - let slice = (*b).as_mut(); - (slice.len(), slice.as_mut_ptr()) - }; - let user_data = Box::into_raw(b); - from_glib_full(ffi::gst_buffer_new_wrapped_full( - 0, - data as glib::ffi::gpointer, - size, - 0, - size, - user_data as glib::ffi::gpointer, - Some(Self::drop_box::), - )) + let mem = Memory::from_mut_slice(slice); + let mut buffer = Buffer::new(); + { + let buffer = buffer.get_mut().unwrap(); + buffer.append_memory(mem); + buffer.unset_flags(BufferFlags::TAG_MEMORY); } + + buffer } + #[doc(alias = "gst_buffer_new_wrapped")] #[doc(alias = "gst_buffer_new_wrapped_full")] + #[inline] pub fn from_slice + Send + 'static>(slice: T) -> Self { assert_initialized_main_thread!(); - unsafe { - let b = Box::new(slice); - let (size, data) = { - let slice = (*b).as_ref(); - (slice.len(), slice.as_ptr()) - }; - let user_data = Box::into_raw(b); - from_glib_full(ffi::gst_buffer_new_wrapped_full( - ffi::GST_MEMORY_FLAG_READONLY, - data as glib::ffi::gpointer, - size, - 0, - size, - user_data as glib::ffi::gpointer, - Some(Self::drop_box::), - )) + let mem = Memory::from_slice(slice); + let mut buffer = Buffer::new(); + { + let buffer = buffer.get_mut().unwrap(); + buffer.append_memory(mem); + buffer.unset_flags(BufferFlags::TAG_MEMORY); } + + buffer } #[doc(alias = "gst_buffer_map")] diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 0b1fa3dd0..0790c5005 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -121,6 +121,7 @@ pub use crate::buffer::{ mod buffer_cursor; pub use crate::buffer_cursor::{BufferCursor, BufferRefCursor}; pub mod memory; +mod memory_wrapped; pub use crate::memory::{MappedMemory, Memory, MemoryMap, MemoryRef}; #[cfg(feature = "serde")] mod buffer_serde; diff --git a/gstreamer/src/memory.rs b/gstreamer/src/memory.rs index 7e65b0d35..e6ebf5d3f 100644 --- a/gstreamer/src/memory.rs +++ b/gstreamer/src/memory.rs @@ -53,11 +53,7 @@ pub enum Readable {} pub enum Writable {} impl Memory { - unsafe extern "C" fn drop_box(vec: glib::ffi::gpointer) { - let slice: Box = Box::from_raw(vec as *mut T); - drop(slice); - } - + #[inline] pub fn with_size(size: usize) -> Self { assert_initialized_main_thread!(); unsafe { @@ -69,6 +65,7 @@ impl Memory { } } + #[inline] pub fn with_size_and_params(size: usize, params: &AllocationParams) -> Self { assert_initialized_main_thread!(); unsafe { @@ -80,49 +77,6 @@ impl Memory { } } - pub fn from_slice + Send + 'static>(slice: T) -> Self { - assert_initialized_main_thread!(); - unsafe { - let b = Box::new(slice); - let (size, data) = { - let slice = (*b).as_ref(); - (slice.len(), slice.as_ptr()) - }; - let user_data = Box::into_raw(b); - from_glib_full(ffi::gst_memory_new_wrapped( - ffi::GST_MEMORY_FLAG_READONLY, - data as glib::ffi::gpointer, - size, - 0, - size, - user_data as glib::ffi::gpointer, - Some(Self::drop_box::), - )) - } - } - - pub fn from_mut_slice + Send + 'static>(slice: T) -> Self { - assert_initialized_main_thread!(); - - unsafe { - let mut b = Box::new(slice); - let (size, data) = { - let slice = (*b).as_mut(); - (slice.len(), slice.as_mut_ptr()) - }; - let user_data = Box::into_raw(b); - from_glib_full(ffi::gst_memory_new_wrapped( - 0, - data as glib::ffi::gpointer, - size, - 0, - size, - user_data as glib::ffi::gpointer, - Some(Self::drop_box::), - )) - } - } - #[inline] pub fn into_mapped_memory_readable(self) -> Result, Self> { unsafe { @@ -948,6 +902,22 @@ mod tests { assert_eq!(map.as_slice(), &[1, 2, 3, 4]); } + #[test] + fn test_share() { + crate::init().unwrap(); + + let mem = crate::Memory::from_slice(vec![1, 2, 3, 4]); + let sub = mem.share(1, Some(2)); + + let map = mem.map_readable().unwrap(); + assert_eq!(map.as_slice(), &[1, 2, 3, 4]); + drop(map); + + let map = sub.map_readable().unwrap(); + assert_eq!(map.as_slice(), &[2, 3]); + drop(map); + } + #[test] fn test_dump() { crate::init().unwrap(); diff --git a/gstreamer/src/memory_wrapped.rs b/gstreamer/src/memory_wrapped.rs new file mode 100644 index 000000000..3f3c828a1 --- /dev/null +++ b/gstreamer/src/memory_wrapped.rs @@ -0,0 +1,278 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::{once_cell::sync::Lazy, translate::*, StaticType}; + +use std::{alloc, mem, ptr}; + +use crate::Memory; + +#[repr(C)] +struct WrappedMemory { + mem: ffi::GstMemory, + + // AsRef / AsMut values + data: *mut u8, + + // Layout used for allocating this struct, literally `Layout::new` + layout: alloc::Layout, + + // Offset from the beginning of the struct until `wrap` + wrap_offset: usize, + // `ptr::drop_in_place()` for `T` + wrap_drop_in_place: unsafe fn(*mut T), + wrap: T, +} + +unsafe extern "C" fn free(_allocator: *mut ffi::GstAllocator, mem: *mut ffi::GstMemory) { + let mem = mem as *mut WrappedMemory<()>; + + if (*mem).wrap_offset > 0 { + let wrap = (mem as *mut u8).add((*mem).wrap_offset) as *mut (); + ((*mem).wrap_drop_in_place)(wrap); + } + + alloc::dealloc(mem as *mut u8, (*mem).layout); +} + +unsafe extern "C" fn mem_map( + mem: *mut ffi::GstMemory, + _maxsize: usize, + _flags: ffi::GstMapFlags, +) -> glib::ffi::gpointer { + let mem = mem as *mut WrappedMemory<()>; + + (*mem).data as glib::ffi::gpointer +} + +unsafe extern "C" fn mem_unmap(_mem: *mut ffi::GstMemory) {} + +unsafe extern "C" fn mem_share( + mem: *mut ffi::GstMemory, + offset: isize, + size: isize, +) -> *mut ffi::GstMemory { + let mem = mem as *mut WrappedMemory<()>; + + // Basically a re-implementation of _sysmem_share() + + let parent = if (*mem).mem.parent.is_null() { + mem + } else { + (*mem).mem.parent as *mut WrappedMemory<()> + }; + + // Actually an usize + let mut size = size as usize; + if size == usize::MAX { + if offset < 0 { + size = (*mem).mem.size + (-offset as usize); + } else { + debug_assert!((*mem).mem.size >= offset as usize); + size = (*mem).mem.size - offset as usize; + } + } + + let new_offset = if offset < 0 { + debug_assert!((*mem).mem.offset >= (-offset as usize)); + (*mem).mem.offset - (-offset as usize) + } else { + (*mem).mem.offset + offset as usize + }; + + debug_assert!(new_offset + size <= (*mem).mem.maxsize); + + let layout = alloc::Layout::new::>(); + let sub = alloc::alloc(layout) as *mut WrappedMemory<()>; + + ffi::gst_memory_init( + sub as *mut ffi::GstMemory, + (*mem).mem.mini_object.flags | ffi::GST_MINI_OBJECT_FLAG_LOCK_READONLY, + (*mem).mem.allocator, + parent as *mut ffi::GstMemory, + (*mem).mem.maxsize, + (*mem).mem.align, + new_offset, + size, + ); + ptr::write(ptr::addr_of_mut!((*sub).data), (*mem).data); + ptr::write(ptr::addr_of_mut!((*sub).layout), layout); + ptr::write(ptr::addr_of_mut!((*sub).wrap_offset), 0); + ptr::write(ptr::addr_of_mut!((*sub).wrap_drop_in_place), |_| ()); + + sub as *mut ffi::GstMemory +} + +unsafe extern "C" fn mem_is_span( + mem1: *mut ffi::GstMemory, + mem2: *mut ffi::GstMemory, + offset: *mut usize, +) -> glib::ffi::gboolean { + let mem1 = mem1 as *mut WrappedMemory<()>; + let mem2 = mem2 as *mut WrappedMemory<()>; + + // Basically a re-implementation of _sysmem_is_span() + if !offset.is_null() { + let parent = (*mem1).mem.parent as *mut WrappedMemory<()>; + *offset = (*mem1).mem.offset - (*parent).mem.offset; + } + + let is_span = (*mem1).data.add((*mem1).mem.offset).add((*mem1).mem.size) + == (*mem2).data.add((*mem2).mem.offset); + + is_span.into_glib() +} + +unsafe extern "C" fn class_init(class: glib::ffi::gpointer, _class_data: glib::ffi::gpointer) { + let class = class as *mut ffi::GstAllocatorClass; + + (*class).free = Some(free); +} + +unsafe extern "C" fn instance_init( + obj: *mut glib::gobject_ffi::GTypeInstance, + _class: glib::ffi::gpointer, +) { + static ALLOCATOR_TYPE: &[u8] = b"RustGlobalAllocatorMemory\0"; + + let allocator = obj as *mut ffi::GstAllocator; + + (*allocator).mem_type = ALLOCATOR_TYPE.as_ptr() as *const _; + (*allocator).mem_map = Some(mem_map); + (*allocator).mem_unmap = Some(mem_unmap); + // mem_copy not set because the fallback already does the right thing + (*allocator).mem_share = Some(mem_share); + (*allocator).mem_is_span = Some(mem_is_span); + + // TODO: Could also implement alloc() + (*allocator).object.flags |= ffi::GST_ALLOCATOR_FLAG_CUSTOM_ALLOC; +} + +static RUST_ALLOCATOR: Lazy = Lazy::new(|| unsafe { + struct TypeInfoWrap(glib::gobject_ffi::GTypeInfo); + unsafe impl Send for TypeInfoWrap {} + unsafe impl Sync for TypeInfoWrap {} + + static TYPE_INFO: TypeInfoWrap = TypeInfoWrap(glib::gobject_ffi::GTypeInfo { + class_size: mem::size_of::() as u16, + base_init: None, + base_finalize: None, + class_init: Some(class_init), + class_finalize: None, + class_data: ptr::null_mut(), + instance_size: mem::size_of::() as u16, + n_preallocs: 0, + instance_init: Some(instance_init), + value_table: ptr::null(), + }); + + let type_name = { + let mut idx = 0; + + loop { + let type_name = glib::gformat!("GstRsAllocator-{}", idx); + if glib::gobject_ffi::g_type_from_name(type_name.as_ptr()) + == glib::gobject_ffi::G_TYPE_INVALID + { + break type_name; + } + idx += 1; + } + }; + + let t = glib::gobject_ffi::g_type_register_static( + crate::Allocator::static_type().into_glib(), + type_name.as_ptr(), + &TYPE_INFO.0, + 0, + ); + + assert!(t != glib::gobject_ffi::G_TYPE_INVALID); + + from_glib_none(glib::gobject_ffi::g_object_newv(t, 0, ptr::null_mut()) as *mut ffi::GstAllocator) +}); + +impl Memory { + #[doc(alias = "gst_memory_new_wrapped")] + #[doc(alias = "gst_memory_new_wrapped_full")] + #[inline] + pub fn from_slice + Send + 'static>(slice: T) -> Self { + assert_initialized_main_thread!(); + + let len = slice.as_ref().len(); + unsafe { + let layout = alloc::Layout::new::>(); + let mem = alloc::alloc(layout) as *mut WrappedMemory; + + ffi::gst_memory_init( + mem as *mut ffi::GstMemory, + ffi::GST_MINI_OBJECT_FLAG_LOCK_READONLY, + RUST_ALLOCATOR.to_glib_none().0, + ptr::null_mut(), + len, + 0, + 0, + len, + ); + + ptr::write(ptr::addr_of_mut!((*mem).wrap), slice); + + assert_eq!(len, (*mem).wrap.as_ref().len()); + let data = (*mem).wrap.as_ref().as_ptr(); + ptr::write(ptr::addr_of_mut!((*mem).data), mut_override(data)); + + ptr::write(ptr::addr_of_mut!((*mem).layout), layout); + + let wrap_offset = ptr::addr_of!((*mem).wrap) as usize - mem as usize; + ptr::write(ptr::addr_of_mut!((*mem).wrap_offset), wrap_offset); + + ptr::write( + ptr::addr_of_mut!((*mem).wrap_drop_in_place), + ptr::drop_in_place::, + ); + + from_glib_full(mem as *mut ffi::GstMemory) + } + } + + #[doc(alias = "gst_memory_new_wrapped")] + #[doc(alias = "gst_memory_new_wrapped_full")] + #[inline] + pub fn from_mut_slice + Send + 'static>(mut slice: T) -> Self { + assert_initialized_main_thread!(); + + let len = slice.as_mut().len(); + unsafe { + let layout = alloc::Layout::new::>(); + let mem = alloc::alloc(layout) as *mut WrappedMemory; + + ffi::gst_memory_init( + mem as *mut ffi::GstMemory, + 0, + RUST_ALLOCATOR.to_glib_none().0, + ptr::null_mut(), + len, + 0, + 0, + len, + ); + + ptr::write(ptr::addr_of_mut!((*mem).wrap), slice); + + assert_eq!(len, (*mem).wrap.as_mut().len()); + let data = (*mem).wrap.as_mut().as_mut_ptr(); + ptr::write(ptr::addr_of_mut!((*mem).data), data); + + ptr::write(ptr::addr_of_mut!((*mem).layout), layout); + + let wrap_offset = ptr::addr_of!((*mem).wrap) as usize - mem as usize; + ptr::write(ptr::addr_of_mut!((*mem).wrap_offset), wrap_offset); + + ptr::write( + ptr::addr_of_mut!((*mem).wrap_drop_in_place), + ptr::drop_in_place::, + ); + + from_glib_full(mem as *mut ffi::GstMemory) + } + } +}