gstreamer-rs/gstreamer/src/buffer.rs
Sebastian Dröge ceed45cfd7 gstreamer: Optimize Buffer / Memory from_slice() to have fewer allocations
Instead of using `gst_memory_new_wrapped_full()` and boxing the data,
create an GstAllocator subclass that allows allocating memories that
store the data inline.

By avoiding the box, one additional heap allocation per memory is
avoided.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/498

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1357>
2023-12-05 10:51:28 +02:00

1583 lines
45 KiB
Rust

// Take a look at the license at the top of the repository in the LICENSE file.
use std::{fmt, marker::PhantomData, mem, ops, ops::ControlFlow, ptr, slice, u64, usize};
use glib::translate::*;
use crate::{meta::*, BufferCursor, BufferFlags, BufferRefCursor, ClockTime, Memory, MemoryRef};
pub enum Readable {}
pub enum Writable {}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BufferMetaForeachAction {
Keep,
Remove,
}
mini_object_wrapper!(Buffer, BufferRef, ffi::GstBuffer, || {
ffi::gst_buffer_get_type()
});
pub struct BufferMap<'a, T> {
buffer: &'a BufferRef,
map_info: ffi::GstMapInfo,
phantom: PhantomData<T>,
}
pub struct MappedBuffer<T> {
buffer: Buffer,
map_info: ffi::GstMapInfo,
phantom: PhantomData<T>,
}
impl Buffer {
#[doc(alias = "gst_buffer_new")]
#[inline]
pub fn new() -> Self {
assert_initialized_main_thread!();
unsafe { from_glib_full(ffi::gst_buffer_new()) }
}
#[doc(alias = "gst_buffer_new_allocate")]
#[doc(alias = "gst_buffer_new_and_alloc")]
#[inline]
pub fn with_size(size: usize) -> Result<Self, glib::BoolError> {
assert_initialized_main_thread!();
unsafe {
Option::<_>::from_glib_full(ffi::gst_buffer_new_allocate(
ptr::null_mut(),
size,
ptr::null_mut(),
))
.ok_or_else(|| glib::bool_error!("Failed to allocate buffer"))
}
}
#[doc(alias = "gst_buffer_new_wrapped")]
#[doc(alias = "gst_buffer_new_wrapped_full")]
#[inline]
pub fn from_mut_slice<T: AsMut<[u8]> + Send + 'static>(slice: T) -> Self {
assert_initialized_main_thread!();
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<T: AsRef<[u8]> + Send + 'static>(slice: T) -> Self {
assert_initialized_main_thread!();
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")]
#[inline]
pub fn into_mapped_buffer_readable(self) -> Result<MappedBuffer<Readable>, Self> {
unsafe {
let mut map_info = mem::MaybeUninit::uninit();
let res: bool = from_glib(ffi::gst_buffer_map(
self.as_mut_ptr(),
map_info.as_mut_ptr(),
ffi::GST_MAP_READ,
));
if res {
Ok(MappedBuffer {
buffer: self,
map_info: map_info.assume_init(),
phantom: PhantomData,
})
} else {
Err(self)
}
}
}
#[doc(alias = "gst_buffer_map")]
#[inline]
pub fn into_mapped_buffer_writable(self) -> Result<MappedBuffer<Writable>, Self> {
unsafe {
let mut map_info = mem::MaybeUninit::uninit();
let res: bool = from_glib(ffi::gst_buffer_map(
self.as_mut_ptr(),
map_info.as_mut_ptr(),
ffi::GST_MAP_READWRITE,
));
if res {
Ok(MappedBuffer {
buffer: self,
map_info: map_info.assume_init(),
phantom: PhantomData,
})
} else {
Err(self)
}
}
}
#[inline]
pub fn into_cursor_readable(self) -> BufferCursor<Readable> {
BufferCursor::new_readable(self)
}
#[inline]
pub fn into_cursor_writable(self) -> Result<BufferCursor<Writable>, glib::BoolError> {
BufferCursor::new_writable(self)
}
#[doc(alias = "gst_buffer_append")]
pub fn append(&mut self, other: Self) {
unsafe {
let ptr = ffi::gst_buffer_append(self.as_mut_ptr(), other.into_glib_ptr());
self.replace_ptr(ptr);
}
}
}
impl Default for Buffer {
fn default() -> Self {
Self::new()
}
}
impl BufferRef {
#[doc(alias = "gst_buffer_map")]
#[inline]
pub fn map_readable(&self) -> Result<BufferMap<Readable>, glib::BoolError> {
unsafe {
let mut map_info = mem::MaybeUninit::uninit();
let res =
ffi::gst_buffer_map(self.as_mut_ptr(), map_info.as_mut_ptr(), ffi::GST_MAP_READ);
if res == glib::ffi::GTRUE {
Ok(BufferMap {
buffer: self,
map_info: map_info.assume_init(),
phantom: PhantomData,
})
} else {
Err(glib::bool_error!("Failed to map buffer readable"))
}
}
}
#[doc(alias = "gst_buffer_map")]
#[inline]
pub fn map_writable(&mut self) -> Result<BufferMap<Writable>, glib::BoolError> {
unsafe {
let mut map_info = mem::MaybeUninit::uninit();
let res = ffi::gst_buffer_map(
self.as_mut_ptr(),
map_info.as_mut_ptr(),
ffi::GST_MAP_READWRITE,
);
if res == glib::ffi::GTRUE {
Ok(BufferMap {
buffer: self,
map_info: map_info.assume_init(),
phantom: PhantomData,
})
} else {
Err(glib::bool_error!("Failed to map buffer writable"))
}
}
}
#[doc(alias = "gst_buffer_copy_region")]
pub fn copy_region(
&self,
flags: crate::BufferCopyFlags,
offset: usize,
size: Option<usize>,
) -> Result<Buffer, glib::BoolError> {
let size_real = size.unwrap_or(usize::MAX);
unsafe {
Option::<_>::from_glib_full(ffi::gst_buffer_copy_region(
self.as_mut_ptr(),
flags.into_glib(),
offset,
size_real,
))
.ok_or_else(|| glib::bool_error!("Failed to copy region of buffer"))
}
}
#[doc(alias = "gst_buffer_copy_into")]
pub fn copy_into(
&self,
dest: &mut BufferRef,
flags: crate::BufferCopyFlags,
offset: usize,
size: Option<usize>,
) -> Result<(), glib::BoolError> {
let size_real = size.unwrap_or(usize::MAX);
unsafe {
glib::result_from_gboolean!(
ffi::gst_buffer_copy_into(
dest.as_mut_ptr(),
self.as_mut_ptr(),
flags.into_glib(),
offset,
size_real,
),
"Failed to copy into destination buffer",
)
}
}
#[doc(alias = "gst_buffer_fill")]
pub fn copy_from_slice(&mut self, offset: usize, slice: &[u8]) -> Result<(), usize> {
let maxsize = self.maxsize();
let size = slice.len();
assert!(maxsize >= offset && maxsize - offset >= size);
let copied = unsafe {
let src = slice.as_ptr();
ffi::gst_buffer_fill(
self.as_mut_ptr(),
offset,
src as glib::ffi::gconstpointer,
size,
)
};
if copied == size {
Ok(())
} else {
Err(copied)
}
}
#[doc(alias = "gst_buffer_extract")]
pub fn copy_to_slice(&self, offset: usize, slice: &mut [u8]) -> Result<(), usize> {
let maxsize = self.size();
let size = slice.len();
assert!(maxsize >= offset && maxsize - offset >= size);
let copied = unsafe {
let dest = slice.as_mut_ptr();
ffi::gst_buffer_extract(self.as_mut_ptr(), offset, dest as glib::ffi::gpointer, size)
};
if copied == size {
Ok(())
} else {
Err(copied)
}
}
#[doc(alias = "gst_buffer_copy_deep")]
pub fn copy_deep(&self) -> Result<Buffer, glib::BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::gst_buffer_copy_deep(self.as_ptr()))
.ok_or_else(|| glib::bool_error!("Failed to deep copy buffer"))
}
}
#[doc(alias = "get_size")]
#[doc(alias = "gst_buffer_get_size")]
pub fn size(&self) -> usize {
unsafe { ffi::gst_buffer_get_size(self.as_mut_ptr()) }
}
#[doc(alias = "get_maxsize")]
pub fn maxsize(&self) -> usize {
unsafe {
let mut maxsize = mem::MaybeUninit::uninit();
ffi::gst_buffer_get_sizes_range(
self.as_mut_ptr(),
0,
-1,
ptr::null_mut(),
maxsize.as_mut_ptr(),
);
maxsize.assume_init()
}
}
#[doc(alias = "gst_buffer_set_size")]
pub fn set_size(&mut self, size: usize) {
assert!(self.maxsize() >= size);
unsafe {
ffi::gst_buffer_set_size(self.as_mut_ptr(), size as isize);
}
}
#[doc(alias = "get_offset")]
#[doc(alias = "GST_BUFFER_OFFSET")]
#[inline]
pub fn offset(&self) -> u64 {
self.0.offset
}
#[inline]
pub fn set_offset(&mut self, offset: u64) {
self.0.offset = offset;
}
#[doc(alias = "get_offset_end")]
#[doc(alias = "GST_BUFFER_OFFSET_END")]
#[inline]
pub fn offset_end(&self) -> u64 {
self.0.offset_end
}
#[inline]
pub fn set_offset_end(&mut self, offset_end: u64) {
self.0.offset_end = offset_end;
}
#[doc(alias = "get_pts")]
#[doc(alias = "GST_BUFFER_PTS")]
#[inline]
pub fn pts(&self) -> Option<ClockTime> {
unsafe { from_glib(self.0.pts) }
}
#[inline]
pub fn set_pts(&mut self, pts: impl Into<Option<ClockTime>>) {
self.0.pts = pts.into().into_glib();
}
#[doc(alias = "get_dts")]
#[doc(alias = "GST_BUFFER_DTS")]
#[inline]
pub fn dts(&self) -> Option<ClockTime> {
unsafe { from_glib(self.0.dts) }
}
#[inline]
pub fn set_dts(&mut self, dts: impl Into<Option<ClockTime>>) {
self.0.dts = dts.into().into_glib();
}
#[doc(alias = "get_dts_or_pts")]
#[doc(alias = "GST_BUFFER_DTS_OR_PTS")]
#[inline]
pub fn dts_or_pts(&self) -> Option<ClockTime> {
let val = self.dts();
if val.is_none() {
self.pts()
} else {
val
}
}
#[doc(alias = "get_duration")]
#[doc(alias = "GST_BUFFER_DURATION")]
#[inline]
pub fn duration(&self) -> Option<ClockTime> {
unsafe { from_glib(self.0.duration) }
}
#[inline]
pub fn set_duration(&mut self, duration: impl Into<Option<ClockTime>>) {
self.0.duration = duration.into().into_glib();
}
#[doc(alias = "get_flags")]
#[doc(alias = "GST_BUFFER_FLAGS")]
#[inline]
pub fn flags(&self) -> BufferFlags {
BufferFlags::from_bits_truncate(self.0.mini_object.flags)
}
#[doc(alias = "GST_BUFFER_FLAG_SET")]
#[inline]
pub fn set_flags(&mut self, flags: BufferFlags) {
self.0.mini_object.flags |= flags.bits();
}
#[doc(alias = "GST_BUFFER_FLAG_UNSET")]
#[inline]
pub fn unset_flags(&mut self, flags: BufferFlags) {
self.0.mini_object.flags &= !flags.bits();
}
#[doc(alias = "get_meta")]
#[doc(alias = "gst_buffer_get_meta")]
#[inline]
pub fn meta<T: MetaAPI>(&self) -> Option<MetaRef<T>> {
unsafe {
let meta = ffi::gst_buffer_get_meta(self.as_mut_ptr(), T::meta_api().into_glib());
if meta.is_null() {
None
} else {
Some(T::from_ptr(self, meta as *const <T as MetaAPI>::GstType))
}
}
}
#[doc(alias = "get_meta_mut")]
#[inline]
pub fn meta_mut<T: MetaAPI>(&mut self) -> Option<MetaRefMut<T, crate::meta::Standalone>> {
unsafe {
let meta = ffi::gst_buffer_get_meta(self.as_mut_ptr(), T::meta_api().into_glib());
if meta.is_null() {
None
} else {
Some(T::from_mut_ptr(self, meta as *mut <T as MetaAPI>::GstType))
}
}
}
pub fn iter_meta<T: MetaAPI>(&self) -> MetaIter<T> {
MetaIter::new(self)
}
pub fn iter_meta_mut<T: MetaAPI>(&mut self) -> MetaIterMut<T> {
MetaIterMut::new(self)
}
#[doc(alias = "gst_buffer_foreach_meta")]
pub fn foreach_meta<F: FnMut(MetaRef<Meta>) -> ControlFlow<(), ()>>(&self, func: F) -> bool {
unsafe extern "C" fn trampoline<F: FnMut(MetaRef<Meta>) -> ControlFlow<(), ()>>(
buffer: *mut ffi::GstBuffer,
meta: *mut *mut ffi::GstMeta,
user_data: glib::ffi::gpointer,
) -> glib::ffi::gboolean {
let func = user_data as *mut F;
let res = (*func)(Meta::from_ptr(BufferRef::from_ptr(buffer), *meta));
matches!(res, ControlFlow::Continue(_)).into_glib()
}
unsafe {
let func_ptr: &F = &func;
from_glib(ffi::gst_buffer_foreach_meta(
mut_override(self.as_ptr()),
Some(trampoline::<F>),
func_ptr as *const _ as *mut _,
))
}
}
#[doc(alias = "gst_buffer_foreach_meta")]
pub fn foreach_meta_mut<
F: FnMut(
MetaRefMut<Meta, crate::meta::Iterated>,
) -> ControlFlow<BufferMetaForeachAction, BufferMetaForeachAction>,
>(
&mut self,
func: F,
) -> bool {
unsafe extern "C" fn trampoline<
F: FnMut(
MetaRefMut<Meta, crate::meta::Iterated>,
) -> ControlFlow<BufferMetaForeachAction, BufferMetaForeachAction>,
>(
buffer: *mut ffi::GstBuffer,
meta: *mut *mut ffi::GstMeta,
user_data: glib::ffi::gpointer,
) -> glib::ffi::gboolean {
let func = user_data as *mut F;
let res = (*func)(Meta::from_mut_ptr(BufferRef::from_mut_ptr(buffer), *meta));
let (cont, action) = match res {
ControlFlow::Continue(action) => (true, action),
ControlFlow::Break(action) => (false, action),
};
if action == BufferMetaForeachAction::Remove {
*meta = ptr::null_mut();
}
cont.into_glib()
}
unsafe {
let func_ptr: &F = &func;
from_glib(ffi::gst_buffer_foreach_meta(
mut_override(self.as_ptr()),
Some(trampoline::<F>),
func_ptr as *const _ as *mut _,
))
}
}
#[doc(alias = "gst_buffer_append_memory")]
pub fn append_memory(&mut self, mem: Memory) {
unsafe { ffi::gst_buffer_append_memory(self.as_mut_ptr(), mem.into_glib_ptr()) }
}
#[doc(alias = "gst_buffer_find_memory")]
pub fn find_memory(&self, offset: usize, size: Option<usize>) -> Option<(u32, u32, usize)> {
unsafe {
let mut idx = mem::MaybeUninit::uninit();
let mut length = mem::MaybeUninit::uninit();
let mut skip = mem::MaybeUninit::uninit();
let res = from_glib(ffi::gst_buffer_find_memory(
self.as_mut_ptr(),
offset,
size.unwrap_or(usize::MAX),
idx.as_mut_ptr(),
length.as_mut_ptr(),
skip.as_mut_ptr(),
));
if res {
Some((idx.assume_init(), length.assume_init(), skip.assume_init()))
} else {
None
}
}
}
#[doc(alias = "get_all_memory")]
#[doc(alias = "gst_buffer_get_all_memory")]
pub fn all_memory(&self) -> Option<Memory> {
unsafe {
let res = ffi::gst_buffer_get_all_memory(self.as_mut_ptr());
if res.is_null() {
None
} else {
Some(from_glib_full(res))
}
}
}
#[doc(alias = "get_max_memory")]
#[doc(alias = "gst_buffer_get_max_memory")]
pub fn max_memory() -> u32 {
unsafe { ffi::gst_buffer_get_max_memory() }
}
#[doc(alias = "get_memory")]
#[doc(alias = "gst_buffer_get_memory")]
pub fn memory(&self, idx: u32) -> Option<Memory> {
if idx >= self.n_memory() {
None
} else {
unsafe {
let res = ffi::gst_buffer_get_memory(self.as_mut_ptr(), idx);
if res.is_null() {
None
} else {
Some(from_glib_full(res))
}
}
}
}
#[doc(alias = "get_memory_range")]
#[doc(alias = "gst_buffer_get_memory_range")]
pub fn memory_range(&self, idx: u32, length: Option<u32>) -> Option<Memory> {
assert!(idx + length.unwrap_or(0) < self.n_memory());
unsafe {
let res = ffi::gst_buffer_get_memory_range(
self.as_mut_ptr(),
idx,
match length {
Some(val) => val as i32,
None => -1,
},
);
if res.is_null() {
None
} else {
Some(from_glib_full(res))
}
}
}
#[doc(alias = "gst_buffer_insert_memory")]
pub fn insert_memory(&mut self, idx: Option<u32>, mem: Memory) {
unsafe {
ffi::gst_buffer_insert_memory(
self.as_mut_ptr(),
match idx {
Some(val) => val as i32,
None => -1,
},
mem.into_glib_ptr(),
)
}
}
#[doc(alias = "gst_buffer_is_all_memory_writable")]
pub fn is_all_memory_writable(&self) -> bool {
unsafe { from_glib(ffi::gst_buffer_is_all_memory_writable(self.as_mut_ptr())) }
}
#[doc(alias = "gst_buffer_is_memory_range_writable")]
pub fn is_memory_range_writable(&self, idx: u32, length: Option<u16>) -> bool {
unsafe {
from_glib(ffi::gst_buffer_is_memory_range_writable(
self.as_mut_ptr(),
idx,
match length {
Some(val) => val as i32,
None => -1,
},
))
}
}
#[doc(alias = "gst_buffer_n_memory")]
pub fn n_memory(&self) -> u32 {
unsafe { ffi::gst_buffer_n_memory(self.as_ptr() as *mut _) }
}
#[doc(alias = "gst_buffer_peek_memory")]
pub fn peek_memory(&self, idx: u32) -> &MemoryRef {
assert!(idx < self.n_memory());
unsafe { MemoryRef::from_ptr(ffi::gst_buffer_peek_memory(self.as_mut_ptr(), idx)) }
}
#[doc(alias = "gst_buffer_peek_memory")]
pub fn peek_memory_mut(&mut self, idx: u32) -> Result<&mut MemoryRef, glib::BoolError> {
assert!(idx < self.n_memory());
unsafe {
let mem = ffi::gst_buffer_peek_memory(self.as_mut_ptr(), idx);
if ffi::gst_mini_object_is_writable(mem as *mut _) == glib::ffi::GFALSE {
Err(glib::bool_error!("Memory not writable"))
} else {
Ok(MemoryRef::from_mut_ptr(ffi::gst_buffer_peek_memory(
self.as_mut_ptr(),
idx,
)))
}
}
}
#[doc(alias = "gst_buffer_prepend_memory")]
pub fn prepend_memory(&mut self, mem: Memory) {
unsafe { ffi::gst_buffer_prepend_memory(self.as_mut_ptr(), mem.into_glib_ptr()) }
}
#[doc(alias = "gst_buffer_remove_all_memory")]
pub fn remove_all_memory(&mut self) {
unsafe { ffi::gst_buffer_remove_all_memory(self.as_mut_ptr()) }
}
#[doc(alias = "gst_buffer_remove_memory")]
pub fn remove_memory(&mut self, idx: u32) {
assert!(idx < self.n_memory());
unsafe { ffi::gst_buffer_remove_memory(self.as_mut_ptr(), idx) }
}
#[doc(alias = "gst_buffer_remove_memory_range")]
pub fn remove_memory_range(&mut self, idx: u32, length: Option<u32>) {
assert!(idx + length.unwrap_or(0) < self.n_memory());
unsafe {
ffi::gst_buffer_remove_memory_range(
self.as_mut_ptr(),
idx,
match length {
Some(val) => val as i32,
None => -1,
},
)
}
}
#[doc(alias = "gst_buffer_replace_all_memory")]
pub fn replace_all_memory(&mut self, mem: Memory) {
unsafe { ffi::gst_buffer_replace_all_memory(self.as_mut_ptr(), mem.into_glib_ptr()) }
}
#[doc(alias = "gst_buffer_replace_memory")]
pub fn replace_memory(&mut self, idx: u32, mem: Memory) {
assert!(idx < self.n_memory());
unsafe { ffi::gst_buffer_replace_memory(self.as_mut_ptr(), idx, mem.into_glib_ptr()) }
}
#[doc(alias = "gst_buffer_replace_memory_range")]
pub fn replace_memory_range(&mut self, idx: u32, length: Option<u32>, mem: Memory) {
assert!(idx + length.unwrap_or(0) < self.n_memory());
unsafe {
ffi::gst_buffer_replace_memory_range(
self.as_mut_ptr(),
idx,
match length {
Some(val) => val as i32,
None => -1,
},
mem.into_glib_ptr(),
)
}
}
pub fn iter_memories(&self) -> Iter {
Iter::new(self)
}
pub fn iter_memories_mut(&mut self) -> Result<IterMut, glib::BoolError> {
if !self.is_all_memory_writable() {
Err(glib::bool_error!("Not all memory are writable"))
} else {
Ok(IterMut::new(self))
}
}
pub fn iter_memories_owned(&self) -> IterOwned {
IterOwned::new(self)
}
pub fn as_cursor_readable(&self) -> BufferRefCursor<&BufferRef> {
BufferRefCursor::new_readable(self)
}
pub fn as_cursor_writable(
&mut self,
) -> Result<BufferRefCursor<&mut BufferRef>, glib::BoolError> {
BufferRefCursor::new_writable(self)
}
}
macro_rules! define_meta_iter(
($name:ident, $typ:ty, $mtyp:ty, $prepare_buffer:expr, $from_ptr:expr) => {
pub struct $name<'a, T: MetaAPI + 'a> {
buffer: $typ,
state: glib::ffi::gpointer,
meta_api: glib::Type,
items: PhantomData<$mtyp>,
}
unsafe impl<'a, T: MetaAPI> Send for $name<'a, T> { }
unsafe impl<'a, T: MetaAPI> Sync for $name<'a, T> { }
impl<'a, T: MetaAPI> fmt::Debug for $name<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(stringify!($name))
.field("buffer", &self.buffer)
.field("state", &self.state)
.field("meta_api", &self.meta_api)
.field("items", &self.items)
.finish()
}
}
impl<'a, T: MetaAPI> $name<'a, T> {
fn new(buffer: $typ) -> $name<'a, T> {
skip_assert_initialized!();
$name {
buffer,
state: ptr::null_mut(),
meta_api: T::meta_api(),
items: PhantomData,
}
}
}
#[allow(clippy::redundant_closure_call)]
impl<'a, T: MetaAPI> Iterator for $name<'a, T> {
type Item = $mtyp;
fn next(&mut self) -> Option<Self::Item> {
loop {
unsafe {
let meta = ffi::gst_buffer_iterate_meta(self.buffer.as_mut_ptr(), &mut self.state);
if meta.is_null() {
return None;
} else if self.meta_api == glib::Type::INVALID || glib::Type::from_glib((*(*meta).info).api) == self.meta_api {
// FIXME: Workaround for a lifetime issue with the mutable iterator only
let buffer = $prepare_buffer(self.buffer.as_mut_ptr());
let item = $from_ptr(buffer, meta);
return Some(item);
}
}
}
}
}
impl<'a, T: MetaAPI> std::iter::FusedIterator for $name<'a, T> { }
}
);
define_meta_iter!(
MetaIter,
&'a BufferRef,
MetaRef<'a, T>,
|buffer: *const ffi::GstBuffer| BufferRef::from_ptr(buffer),
|buffer, meta| T::from_ptr(buffer, meta as *const <T as MetaAPI>::GstType)
);
define_meta_iter!(
MetaIterMut,
&'a mut BufferRef,
MetaRefMut<'a, T, crate::meta::Iterated>,
|buffer: *mut ffi::GstBuffer| BufferRef::from_mut_ptr(buffer),
|buffer: &'a mut BufferRef, meta| T::from_mut_ptr(buffer, meta as *mut <T as MetaAPI>::GstType)
);
macro_rules! define_iter(
($name:ident, $typ:ty, $mtyp:ty, $get_item:expr) => {
pub struct $name<'a> {
buffer: $typ,
idx: usize,
n_memory: usize,
}
impl<'a> fmt::Debug for $name<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(stringify!($name))
.field("buffer", &self.buffer)
.field("idx", &self.idx)
.field("n_memory", &self.n_memory)
.finish()
}
}
impl<'a> $name<'a> {
fn new(buffer: $typ) -> $name<'a> {
skip_assert_initialized!();
let n_memory = buffer.n_memory();
$name {
buffer,
idx: 0,
n_memory: n_memory as usize,
}
}
}
#[allow(clippy::redundant_closure_call)]
impl<'a> Iterator for $name<'a> {
type Item = $mtyp;
fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.n_memory {
return None;
}
#[allow(unused_unsafe)]
unsafe {
let item = $get_item(self.buffer, self.idx as u32).unwrap();
self.idx += 1;
Some(item)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.n_memory - self.idx;
(remaining, Some(remaining))
}
fn count(self) -> usize {
self.n_memory - self.idx
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let (end, overflow) = self.idx.overflowing_add(n);
if end >= self.n_memory || overflow {
self.idx = self.n_memory;
None
} else {
#[allow(unused_unsafe)]
unsafe {
self.idx = end + 1;
Some($get_item(self.buffer, end as u32).unwrap())
}
}
}
fn last(self) -> Option<Self::Item> {
if self.idx == self.n_memory {
None
} else {
#[allow(unused_unsafe)]
unsafe {
Some($get_item(self.buffer, self.n_memory as u32 - 1).unwrap())
}
}
}
}
#[allow(clippy::redundant_closure_call)]
impl<'a> DoubleEndedIterator for $name<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.idx == self.n_memory {
return None;
}
#[allow(unused_unsafe)]
unsafe {
self.n_memory -= 1;
Some($get_item(self.buffer, self.n_memory as u32).unwrap())
}
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let (end, overflow) = self.n_memory.overflowing_sub(n);
if end <= self.idx || overflow {
self.idx = self.n_memory;
None
} else {
#[allow(unused_unsafe)]
unsafe {
self.n_memory = end - 1;
Some($get_item(self.buffer, self.n_memory as u32).unwrap())
}
}
}
}
impl<'a> ExactSizeIterator for $name<'a> {}
impl<'a> std::iter::FusedIterator for $name<'a> {}
}
);
define_iter!(
Iter,
&'a BufferRef,
&'a MemoryRef,
|buffer: &BufferRef, idx| {
let ptr = ffi::gst_buffer_peek_memory(buffer.as_mut_ptr(), idx);
if ptr.is_null() {
None
} else {
Some(MemoryRef::from_ptr(ptr as *const ffi::GstMemory))
}
}
);
define_iter!(
IterMut,
&'a mut BufferRef,
&'a mut MemoryRef,
|buffer: &mut BufferRef, idx| {
let ptr = ffi::gst_buffer_peek_memory(buffer.as_mut_ptr(), idx);
if ptr.is_null() {
None
} else {
Some(MemoryRef::from_mut_ptr(ptr))
}
}
);
impl<'a> IntoIterator for &'a BufferRef {
type IntoIter = Iter<'a>;
type Item = &'a MemoryRef;
fn into_iter(self) -> Self::IntoIter {
self.iter_memories()
}
}
impl From<Memory> for Buffer {
fn from(value: Memory) -> Self {
skip_assert_initialized!();
let mut buffer = Buffer::new();
{
let buffer = buffer.get_mut().unwrap();
buffer.append_memory(value);
}
buffer
}
}
impl<const N: usize> From<[Memory; N]> for Buffer {
fn from(value: [Memory; N]) -> Self {
skip_assert_initialized!();
let mut buffer = Buffer::new();
{
let buffer = buffer.get_mut().unwrap();
value.into_iter().for_each(|b| buffer.append_memory(b));
}
buffer
}
}
impl std::iter::FromIterator<Memory> for Buffer {
fn from_iter<T: IntoIterator<Item = Memory>>(iter: T) -> Self {
skip_assert_initialized!();
let iter = iter.into_iter();
let mut buffer = Buffer::new();
{
let buffer = buffer.get_mut().unwrap();
iter.for_each(|m| buffer.append_memory(m));
}
buffer
}
}
impl std::iter::Extend<Memory> for BufferRef {
fn extend<T: IntoIterator<Item = Memory>>(&mut self, iter: T) {
iter.into_iter().for_each(|m| self.append_memory(m));
}
}
define_iter!(
IterOwned,
&'a BufferRef,
Memory,
|buffer: &BufferRef, idx| { buffer.memory(idx) }
);
impl fmt::Debug for Buffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
BufferRef::fmt(self, f)
}
}
impl PartialEq for Buffer {
fn eq(&self, other: &Buffer) -> bool {
BufferRef::eq(self, other)
}
}
impl Eq for Buffer {}
impl PartialEq<BufferRef> for Buffer {
fn eq(&self, other: &BufferRef) -> bool {
BufferRef::eq(self, other)
}
}
impl PartialEq<Buffer> for BufferRef {
fn eq(&self, other: &Buffer) -> bool {
BufferRef::eq(other, self)
}
}
impl fmt::Debug for BufferRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use std::cell::RefCell;
use crate::utils::Displayable;
struct DebugIter<I>(RefCell<I>);
impl<I: Iterator> fmt::Debug for DebugIter<I>
where
I::Item: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(&mut *self.0.borrow_mut()).finish()
}
}
f.debug_struct("Buffer")
.field("ptr", &self.as_ptr())
.field("pts", &self.pts().display())
.field("dts", &self.dts().display())
.field("duration", &self.duration().display())
.field("size", &self.size())
.field("offset", &self.offset())
.field("offset_end", &self.offset_end())
.field("flags", &self.flags())
.field(
"metas",
&DebugIter(RefCell::new(
self.iter_meta::<crate::Meta>().map(|m| m.api()),
)),
)
.finish()
}
}
impl PartialEq for BufferRef {
fn eq(&self, other: &BufferRef) -> bool {
if self.size() != other.size() {
return false;
}
let self_map = self.map_readable();
let other_map = other.map_readable();
match (self_map, other_map) {
(Ok(self_map), Ok(other_map)) => self_map.as_slice().eq(other_map.as_slice()),
_ => false,
}
}
}
impl Eq for BufferRef {}
impl<'a, T> BufferMap<'a, T> {
#[doc(alias = "get_size")]
#[inline]
pub fn size(&self) -> usize {
self.map_info.size
}
#[doc(alias = "get_buffer")]
#[inline]
pub fn buffer(&self) -> &BufferRef {
self.buffer
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
if self.map_info.size == 0 {
return &[];
}
unsafe { slice::from_raw_parts(self.map_info.data, self.map_info.size) }
}
}
impl<'a> BufferMap<'a, Writable> {
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
if self.map_info.size == 0 {
return &mut [];
}
unsafe { slice::from_raw_parts_mut(self.map_info.data, self.map_info.size) }
}
}
impl<'a, T> AsRef<[u8]> for BufferMap<'a, T> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl<'a> AsMut<[u8]> for BufferMap<'a, Writable> {
#[inline]
fn as_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
impl<'a, T> ops::Deref for BufferMap<'a, T> {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl<'a> ops::DerefMut for BufferMap<'a, Writable> {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
impl<'a, T> fmt::Debug for BufferMap<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("BufferMap").field(&self.buffer()).finish()
}
}
impl<'a, T> PartialEq for BufferMap<'a, T> {
fn eq(&self, other: &BufferMap<'a, T>) -> bool {
self.as_slice().eq(other.as_slice())
}
}
impl<'a, T> Eq for BufferMap<'a, T> {}
impl<'a, T> Drop for BufferMap<'a, T> {
#[inline]
fn drop(&mut self) {
unsafe {
ffi::gst_buffer_unmap(self.buffer.as_mut_ptr(), &mut self.map_info);
}
}
}
unsafe impl<'a, T> Send for BufferMap<'a, T> {}
unsafe impl<'a, T> Sync for BufferMap<'a, T> {}
impl<T> MappedBuffer<T> {
#[inline]
pub fn as_slice(&self) -> &[u8] {
if self.map_info.size == 0 {
return &[];
}
unsafe { slice::from_raw_parts(self.map_info.data, self.map_info.size) }
}
#[doc(alias = "get_size")]
#[inline]
pub fn size(&self) -> usize {
self.map_info.size
}
#[doc(alias = "get_buffer")]
#[inline]
pub fn buffer(&self) -> &BufferRef {
self.buffer.as_ref()
}
#[inline]
pub fn into_buffer(self) -> Buffer {
let mut s = mem::ManuallyDrop::new(self);
let buffer = unsafe { ptr::read(&s.buffer) };
unsafe {
ffi::gst_buffer_unmap(buffer.as_mut_ptr(), &mut s.map_info);
}
buffer
}
}
impl MappedBuffer<Writable> {
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
if self.map_info.size == 0 {
return &mut [];
}
unsafe { slice::from_raw_parts_mut(self.map_info.data, self.map_info.size) }
}
}
impl<T> AsRef<[u8]> for MappedBuffer<T> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl AsMut<[u8]> for MappedBuffer<Writable> {
#[inline]
fn as_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
impl<T> ops::Deref for MappedBuffer<T> {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl ops::DerefMut for MappedBuffer<Writable> {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
impl<T> Drop for MappedBuffer<T> {
#[inline]
fn drop(&mut self) {
unsafe {
ffi::gst_buffer_unmap(self.buffer.as_mut_ptr(), &mut self.map_info);
}
}
}
impl<T> fmt::Debug for MappedBuffer<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("MappedBuffer").field(&self.buffer()).finish()
}
}
impl<T> PartialEq for MappedBuffer<T> {
fn eq(&self, other: &MappedBuffer<T>) -> bool {
self.as_slice().eq(other.as_slice())
}
}
impl<T> Eq for MappedBuffer<T> {}
unsafe impl<T> Send for MappedBuffer<T> {}
unsafe impl<T> Sync for MappedBuffer<T> {}
#[doc(alias = "GST_BUFFER_COPY_METADATA")]
pub const BUFFER_COPY_METADATA: crate::BufferCopyFlags =
crate::BufferCopyFlags::from_bits_truncate(ffi::GST_BUFFER_COPY_METADATA);
#[doc(alias = "GST_BUFFER_COPY_ALL")]
pub const BUFFER_COPY_ALL: crate::BufferCopyFlags =
crate::BufferCopyFlags::from_bits_truncate(ffi::GST_BUFFER_COPY_ALL);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fields() {
crate::init().unwrap();
let mut buffer = Buffer::new();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(ClockTime::NSECOND);
buffer.set_dts(2 * ClockTime::NSECOND);
buffer.set_offset(3);
buffer.set_offset_end(4);
buffer.set_duration(Some(5 * ClockTime::NSECOND));
}
assert_eq!(buffer.pts(), Some(ClockTime::NSECOND));
assert_eq!(buffer.dts(), Some(2 * ClockTime::NSECOND));
assert_eq!(buffer.offset(), 3);
assert_eq!(buffer.offset_end(), 4);
assert_eq!(buffer.duration(), Some(5 * ClockTime::NSECOND));
}
#[test]
fn test_writability() {
crate::init().unwrap();
let mut buffer = Buffer::from_slice(vec![1, 2, 3, 4]);
{
let data = buffer.map_readable().unwrap();
assert_eq!(data.as_slice(), vec![1, 2, 3, 4].as_slice());
}
assert_ne!(buffer.get_mut(), None);
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(Some(ClockTime::NSECOND));
}
let mut buffer2 = buffer.clone();
assert_eq!(buffer.get_mut(), None);
assert_eq!(buffer2.as_ptr(), buffer.as_ptr());
{
let buffer2 = buffer2.make_mut();
assert_ne!(buffer2.as_ptr(), buffer.as_ptr());
buffer2.set_pts(Some(2 * ClockTime::NSECOND));
let mut data = buffer2.map_writable().unwrap();
assert_eq!(data.as_slice(), vec![1, 2, 3, 4].as_slice());
data.as_mut_slice()[0] = 0;
}
assert_eq!(buffer.pts(), Some(ClockTime::NSECOND));
assert_eq!(buffer2.pts(), Some(2 * ClockTime::NSECOND));
{
let data = buffer.map_readable().unwrap();
assert_eq!(data.as_slice(), vec![1, 2, 3, 4].as_slice());
let data = buffer2.map_readable().unwrap();
assert_eq!(data.as_slice(), vec![0, 2, 3, 4].as_slice());
}
}
#[test]
#[allow(clippy::cognitive_complexity)]
fn test_memories() {
crate::init().unwrap();
let mut buffer = Buffer::new();
{
let buffer = buffer.get_mut().unwrap();
buffer.append_memory(crate::Memory::from_mut_slice(vec![0; 5]));
buffer.append_memory(crate::Memory::from_mut_slice(vec![0; 5]));
buffer.append_memory(crate::Memory::from_mut_slice(vec![0; 5]));
buffer.append_memory(crate::Memory::from_mut_slice(vec![0; 5]));
buffer.append_memory(crate::Memory::from_mut_slice(vec![0; 10]));
}
assert!(buffer.is_all_memory_writable());
assert_eq!(buffer.n_memory(), 5);
assert_eq!(buffer.size(), 30);
for i in 0..5 {
{
let mem = buffer.memory(i).unwrap();
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
{
let mem = buffer.peek_memory(i);
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
{
let buffer = buffer.get_mut().unwrap();
let mem = buffer.peek_memory_mut(i).unwrap();
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_writable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
}
{
let buffer = buffer.get_mut().unwrap();
let mut last = 0;
for (i, mem) in buffer.iter_memories_mut().unwrap().enumerate() {
{
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
{
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
{
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_writable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
last = i;
}
assert_eq!(last, 4);
}
let mut last = 0;
for (i, mem) in buffer.iter_memories().enumerate() {
{
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
{
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
last = i;
}
assert_eq!(last, 4);
let mut last = 0;
for (i, mem) in buffer.iter_memories_owned().enumerate() {
{
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
{
assert_eq!(mem.size(), if i < 4 { 5 } else { 10 });
let map = mem.map_readable().unwrap();
assert_eq!(map.size(), if i < 4 { 5 } else { 10 });
}
last = i;
}
assert_eq!(last, 4);
}
#[test]
fn test_meta_foreach() {
crate::init().unwrap();
let mut buffer = Buffer::new();
{
let buffer = buffer.get_mut().unwrap();
crate::ReferenceTimestampMeta::add(
buffer,
&crate::Caps::builder("foo/bar").build(),
ClockTime::ZERO,
ClockTime::NONE,
);
crate::ReferenceTimestampMeta::add(
buffer,
&crate::Caps::builder("foo/bar").build(),
ClockTime::SECOND,
ClockTime::NONE,
);
}
let mut res = vec![];
buffer.foreach_meta(|meta| {
let meta = meta
.downcast_ref::<crate::ReferenceTimestampMeta>()
.unwrap();
res.push(meta.timestamp());
ControlFlow::Continue(())
});
assert_eq!(&[ClockTime::ZERO, ClockTime::SECOND][..], &res[..]);
}
#[test]
fn test_meta_foreach_mut() {
crate::init().unwrap();
let mut buffer = Buffer::new();
{
let buffer = buffer.get_mut().unwrap();
crate::ReferenceTimestampMeta::add(
buffer,
&crate::Caps::builder("foo/bar").build(),
ClockTime::ZERO,
ClockTime::NONE,
);
crate::ReferenceTimestampMeta::add(
buffer,
&crate::Caps::builder("foo/bar").build(),
ClockTime::SECOND,
ClockTime::NONE,
);
}
let mut res = vec![];
buffer.get_mut().unwrap().foreach_meta_mut(|mut meta| {
let meta = meta
.downcast_ref::<crate::ReferenceTimestampMeta>()
.unwrap();
res.push(meta.timestamp());
if meta.timestamp() == ClockTime::SECOND {
ControlFlow::Continue(BufferMetaForeachAction::Remove)
} else {
ControlFlow::Continue(BufferMetaForeachAction::Keep)
}
});
assert_eq!(&[ClockTime::ZERO, ClockTime::SECOND][..], &res[..]);
let mut res = vec![];
buffer.foreach_meta(|meta| {
let meta = meta
.downcast_ref::<crate::ReferenceTimestampMeta>()
.unwrap();
res.push(meta.timestamp());
ControlFlow::Continue(())
});
assert_eq!(&[ClockTime::ZERO][..], &res[..]);
}
#[test]
fn test_ptr_eq() {
crate::init().unwrap();
let buffer1 = Buffer::new();
assert!(BufferRef::ptr_eq(&buffer1, &buffer1));
let buffer2 = Buffer::new();
assert!(!BufferRef::ptr_eq(&buffer1, &buffer2));
}
}