use core::{fmt, slice};
use ffmpeg_next::ffi::{AVBufferRef, av_buffer_ref, av_buffer_unref};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Origin {
PacketPayload,
Foreign,
}
pub struct FfmpegBuffer {
inner: *mut AVBufferRef,
offset: usize,
len: usize,
origin: Origin,
}
unsafe impl Send for FfmpegBuffer {}
impl FfmpegBuffer {
pub(crate) unsafe fn view_of(
buf: *mut AVBufferRef,
offset: usize,
len: usize,
origin: Origin,
) -> Option<Self> {
if buf.is_null() {
return None;
}
let size = unsafe { (*buf).size };
if offset.checked_add(len)? > size {
return None;
}
let new_ref = unsafe { av_buffer_ref(buf) };
if new_ref.is_null() {
return None;
}
Some(Self {
inner: new_ref,
offset,
len,
origin,
})
}
#[inline]
pub(crate) const fn origin(&self) -> Origin {
self.origin
}
pub fn copy_from_slice(bytes: &[u8]) -> Option<Self> {
use ffmpeg_next::ffi::av_buffer_alloc;
let len = bytes.len();
if len == 0 {
return Some(Self::empty());
}
let raw = unsafe { av_buffer_alloc(len as _) };
if raw.is_null() {
return None;
}
unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), (*raw).data, len) };
Some(Self {
inner: raw,
offset: 0,
len,
origin: Origin::Foreign,
})
}
pub(crate) fn from_rows<'a>(
rows: usize,
row_bytes: usize,
mut row: impl FnMut(usize) -> &'a [u8],
) -> Option<Self> {
use ffmpeg_next::ffi::av_buffer_alloc;
let len = rows.checked_mul(row_bytes)?;
if len == 0 {
return Some(Self::empty());
}
let raw = unsafe { av_buffer_alloc(len as _) };
if raw.is_null() {
return None;
}
let out = Self {
inner: raw,
offset: 0,
len,
origin: Origin::Foreign,
};
for index in 0..rows {
let src = row(index);
if src.len() != row_bytes {
return None;
}
unsafe {
core::ptr::copy_nonoverlapping(src.as_ptr(), (*raw).data.add(index * row_bytes), row_bytes);
}
}
Some(out)
}
#[must_use]
pub const fn empty() -> Self {
Self {
inner: core::ptr::null_mut(),
offset: 0,
len: 0,
origin: Origin::Foreign,
}
}
pub(crate) const fn shrink_to(&mut self, len: usize) {
if len < self.len {
self.len = len;
self.origin = Origin::Foreign;
}
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn offset(&self) -> usize {
self.offset
}
pub fn as_ptr(&self) -> *const u8 {
if self.inner.is_null() {
return core::ptr::NonNull::<u8>::dangling().as_ptr();
}
unsafe {
let data = (*self.inner).data;
if data.is_null() {
return core::ptr::NonNull::<u8>::dangling().as_ptr();
}
(data as *const u8).add(self.offset)
}
}
#[inline]
pub const fn as_av_buffer_ref(&self) -> *const AVBufferRef {
self.inner.cast_const()
}
pub fn ptr_eq(&self, other: &Self) -> bool {
if self.inner.is_null() || other.inner.is_null() {
return self.inner == other.inner;
}
unsafe { (*self.inner).buffer == (*other.inner).buffer }
}
pub fn try_clone(&self) -> Option<Self> {
if self.inner.is_null() {
return Some(Self::empty());
}
let new_ref = unsafe { av_buffer_ref(self.inner) };
if new_ref.is_null() {
return None;
}
Some(Self {
inner: new_ref,
offset: self.offset,
len: self.len,
origin: self.origin,
})
}
}
impl Clone for FfmpegBuffer {
fn clone(&self) -> Self {
self
.try_clone()
.expect("FfmpegBuffer::clone: av_buffer_ref returned null (OOM)")
}
}
impl Drop for FfmpegBuffer {
fn drop(&mut self) {
if self.inner.is_null() {
return;
}
unsafe { av_buffer_unref(&mut self.inner) };
}
}
impl AsRef<[u8]> for FfmpegBuffer {
fn as_ref(&self) -> &[u8] {
if self.len == 0 {
return &[];
}
unsafe { slice::from_raw_parts(self.as_ptr(), self.len) }
}
}
impl fmt::Debug for FfmpegBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FfmpegBuffer")
.field("offset", &self.offset)
.field("len", &self.len)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests;