use std::{
ffi::{CStr, CString},
fmt::{self, Display, Formatter},
marker::PhantomData,
ops::{Deref, DerefMut},
os::raw::{c_char, c_int, c_void},
ptr,
slice::{self, Chunks, ChunksMut},
str::FromStr,
};
use crate::time::{TimeBase, Timestamp};
extern "C" {
fn ffw_get_pixel_format_by_name(name: *const c_char) -> c_int;
fn ffw_pixel_format_is_none(format: c_int) -> c_int;
fn ffw_get_pixel_format_name(format: c_int) -> *const c_char;
fn ffw_frame_new_black(pixel_format: c_int, width: c_int, height: c_int) -> *mut c_void;
fn ffw_frame_get_format(frame: *const c_void) -> c_int;
fn ffw_frame_get_width(frame: *const c_void) -> c_int;
fn ffw_frame_get_height(frame: *const c_void) -> c_int;
fn ffw_frame_get_best_effort_timestamp(frame: *const c_void) -> i64;
fn ffw_frame_get_pts(frame: *const c_void) -> i64;
fn ffw_frame_set_pts(frame: *mut c_void, pts: i64);
fn ffw_frame_get_plane_data(frame: *mut c_void, index: usize) -> *mut u8;
fn ffw_frame_get_line_size(frame: *const c_void, plane: usize) -> usize;
fn ffw_frame_get_line_count(frame: *const c_void, plane: usize) -> usize;
fn ffw_frame_clone(frame: *const c_void) -> *mut c_void;
fn ffw_frame_free(frame: *mut c_void);
fn ffw_frame_is_writable(frame: *const c_void) -> c_int;
fn ffw_frame_make_writable(frame: *mut c_void) -> c_int;
fn ffw_frame_get_picture_type(frame: *const c_void) -> c_int;
fn ffw_frame_set_picture_type(frame: *mut c_void, picture_type: c_int);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum PictureType {
None = 0,
I = 1,
P = 2,
B = 3,
S = 4,
Si = 5,
Sp = 6,
Bi = 7,
}
impl PictureType {
pub(crate) fn from_raw(value: c_int) -> Self {
match value {
1 => PictureType::I,
2 => PictureType::P,
3 => PictureType::B,
4 => PictureType::S,
5 => PictureType::Si,
6 => PictureType::Sp,
7 => PictureType::Bi,
_ => PictureType::None,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct UnknownPixelFormat;
impl Display for UnknownPixelFormat {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_str("unknown pixel format")
}
}
impl std::error::Error for UnknownPixelFormat {}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct PixelFormat(c_int);
impl PixelFormat {
pub(crate) fn from_raw(v: c_int) -> Self {
Self(v)
}
pub(crate) fn into_raw(self) -> c_int {
let Self(format) = self;
format
}
pub fn name(self) -> &'static str {
unsafe {
let ptr = ffw_get_pixel_format_name(self.into_raw());
if ptr.is_null() {
panic!("invalid pixel format");
}
let name = CStr::from_ptr(ptr as _);
name.to_str().unwrap()
}
}
}
impl FromStr for PixelFormat {
type Err = UnknownPixelFormat;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let name = CString::new(s).expect("invalid pixel format name");
unsafe {
let format = ffw_get_pixel_format_by_name(name.as_ptr() as _);
if ffw_pixel_format_is_none(format) == 0 {
Ok(Self(format))
} else {
Err(UnknownPixelFormat)
}
}
}
}
pub fn get_pixel_format(name: &str) -> PixelFormat {
PixelFormat::from_str(name).unwrap()
}
pub struct Plane<'a> {
frame: *mut c_void,
index: usize,
phantom: PhantomData<&'a ()>,
}
impl Plane<'_> {
fn new(frame: *mut c_void, index: usize) -> Self {
Self {
frame,
index,
phantom: PhantomData,
}
}
pub fn data(&self) -> &[u8] {
let line_size = self.line_size();
let line_count = self.line_count();
unsafe {
let data = ffw_frame_get_plane_data(self.frame, self.index as _);
slice::from_raw_parts(data, line_size * line_count)
}
}
pub fn data_mut(&mut self) -> &mut [u8] {
let line_size = self.line_size();
let line_count = self.line_count();
unsafe {
let data = ffw_frame_get_plane_data(self.frame, self.index as _);
slice::from_raw_parts_mut(data, line_size * line_count)
}
}
pub fn line(&self, index: usize) -> Option<&[u8]> {
if index < self.line_count() {
let line_size = self.line_size();
let data = self.data();
let offset = index * line_size;
Some(&data[offset..offset + line_size])
} else {
None
}
}
pub fn line_mut(&mut self, index: usize) -> Option<&mut [u8]> {
if index < self.line_count() {
let line_size = self.line_size();
let data = self.data_mut();
let offset = index * line_size;
Some(&mut data[offset..offset + line_size])
} else {
None
}
}
pub fn lines(&self) -> LinesIter {
let line_size = self.line_size();
let data = self.data();
LinesIter::new(data.chunks(line_size))
}
pub fn lines_mut(&mut self) -> LinesIterMut {
let line_size = self.line_size();
let data = self.data_mut();
LinesIterMut::new(data.chunks_mut(line_size))
}
pub fn line_size(&self) -> usize {
unsafe { ffw_frame_get_line_size(self.frame, self.index as _) as _ }
}
pub fn line_count(&self) -> usize {
unsafe { ffw_frame_get_line_count(self.frame, self.index as _) as _ }
}
}
pub struct LinesIter<'a> {
inner: Chunks<'a, u8>,
}
impl<'a> LinesIter<'a> {
fn new(chunks: Chunks<'a, u8>) -> Self {
Self { inner: chunks }
}
}
impl<'a> Iterator for LinesIter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
pub struct LinesIterMut<'a> {
inner: ChunksMut<'a, u8>,
}
impl<'a> LinesIterMut<'a> {
fn new(chunks: ChunksMut<'a, u8>) -> Self {
Self { inner: chunks }
}
}
impl<'a> Iterator for LinesIterMut<'a> {
type Item = &'a mut [u8];
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
pub struct Planes<'a> {
inner: [Plane<'a>; 4],
}
impl<'a> From<&'a VideoFrame> for Planes<'a> {
fn from(frame: &'a VideoFrame) -> Self {
let inner = [
Plane::new(frame.ptr, 0),
Plane::new(frame.ptr, 1),
Plane::new(frame.ptr, 2),
Plane::new(frame.ptr, 3),
];
Self { inner }
}
}
impl<'a> From<&'a VideoFrameMut> for Planes<'a> {
fn from(frame: &'a VideoFrameMut) -> Self {
let inner = [
Plane::new(frame.ptr, 0),
Plane::new(frame.ptr, 1),
Plane::new(frame.ptr, 2),
Plane::new(frame.ptr, 3),
];
Self { inner }
}
}
impl<'a> Deref for Planes<'a> {
type Target = [Plane<'a>];
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub struct PlanesMut<'a> {
inner: [Plane<'a>; 4],
}
impl<'a> From<&'a mut VideoFrameMut> for PlanesMut<'a> {
fn from(frame: &'a mut VideoFrameMut) -> Self {
let inner = [
Plane::new(frame.ptr, 0),
Plane::new(frame.ptr, 1),
Plane::new(frame.ptr, 2),
Plane::new(frame.ptr, 3),
];
Self { inner }
}
}
impl<'a> Deref for PlanesMut<'a> {
type Target = [Plane<'a>];
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a> DerefMut for PlanesMut<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
pub struct VideoFrameMut {
ptr: *mut c_void,
time_base: TimeBase,
}
impl VideoFrameMut {
pub fn black(pixel_format: PixelFormat, width: usize, height: usize) -> Self {
let ptr = unsafe { ffw_frame_new_black(pixel_format.into_raw(), width as _, height as _) };
if ptr.is_null() {
panic!("unable to allocate a video frame");
}
VideoFrameMut {
ptr,
time_base: TimeBase::MICROSECONDS,
}
}
pub fn pixel_format(&self) -> PixelFormat {
unsafe { PixelFormat::from_raw(ffw_frame_get_format(self.ptr)) }
}
pub fn width(&self) -> usize {
unsafe { ffw_frame_get_width(self.ptr) as _ }
}
pub fn height(&self) -> usize {
unsafe { ffw_frame_get_height(self.ptr) as _ }
}
pub fn time_base(&self) -> TimeBase {
self.time_base
}
pub fn with_time_base(mut self, time_base: TimeBase) -> Self {
let new_pts = self.pts().with_time_base(time_base);
unsafe {
ffw_frame_set_pts(self.ptr, new_pts.timestamp());
}
self.time_base = time_base;
self
}
pub fn pts(&self) -> Timestamp {
let pts = unsafe { ffw_frame_get_pts(self.ptr) };
Timestamp::new(pts, self.time_base)
}
pub fn with_pts(self, pts: Timestamp) -> Self {
let pts = pts.with_time_base(self.time_base);
unsafe { ffw_frame_set_pts(self.ptr, pts.timestamp()) }
self
}
pub fn picture_type(&self) -> PictureType {
unsafe { PictureType::from_raw(ffw_frame_get_picture_type(self.ptr)) }
}
pub fn with_picture_type(self, picture_type: PictureType) -> Self {
unsafe { ffw_frame_set_picture_type(self.ptr, picture_type as c_int) };
self
}
pub fn planes(&self) -> Planes {
Planes::from(self)
}
pub fn planes_mut(&mut self) -> PlanesMut {
PlanesMut::from(self)
}
pub fn freeze(mut self) -> VideoFrame {
let ptr = self.ptr;
self.ptr = ptr::null_mut();
VideoFrame {
ptr,
time_base: self.time_base,
}
}
}
impl Drop for VideoFrameMut {
fn drop(&mut self) {
unsafe { ffw_frame_free(self.ptr) }
}
}
unsafe impl Send for VideoFrameMut {}
unsafe impl Sync for VideoFrameMut {}
pub struct VideoFrame {
ptr: *mut c_void,
time_base: TimeBase,
}
impl VideoFrame {
pub(crate) unsafe fn from_raw_ptr(ptr: *mut c_void, time_base: TimeBase) -> Self {
Self { ptr, time_base }
}
pub fn pixel_format(&self) -> PixelFormat {
unsafe { PixelFormat::from_raw(ffw_frame_get_format(self.ptr)) }
}
pub fn width(&self) -> usize {
unsafe { ffw_frame_get_width(self.ptr) as _ }
}
pub fn height(&self) -> usize {
unsafe { ffw_frame_get_height(self.ptr) as _ }
}
pub fn planes(&self) -> Planes {
Planes::from(self)
}
pub fn time_base(&self) -> TimeBase {
self.time_base
}
pub fn with_time_base(mut self, time_base: TimeBase) -> Self {
let new_pts = self.pts().with_time_base(time_base);
unsafe {
ffw_frame_set_pts(self.ptr, new_pts.timestamp());
}
self.time_base = time_base;
self
}
pub fn best_effort_timestamp(&self) -> Timestamp {
let pts = unsafe { ffw_frame_get_best_effort_timestamp(self.ptr) };
Timestamp::new(pts, self.time_base)
}
pub fn pts(&self) -> Timestamp {
let pts = unsafe { ffw_frame_get_pts(self.ptr) };
Timestamp::new(pts, self.time_base)
}
pub fn with_pts(self, pts: Timestamp) -> Self {
let pts = pts.with_time_base(self.time_base);
unsafe { ffw_frame_set_pts(self.ptr, pts.timestamp()) }
self
}
pub fn picture_type(&self) -> PictureType {
unsafe { PictureType::from_raw(ffw_frame_get_picture_type(self.ptr)) }
}
pub(crate) fn as_ptr(&self) -> *const c_void {
self.ptr
}
pub fn try_into_mut(self) -> Result<VideoFrameMut, VideoFrame> {
let res = unsafe { ffw_frame_is_writable(self.ptr) };
if res > 0 {
Ok(self.into_mut())
} else {
Err(self)
}
}
pub fn into_mut(mut self) -> VideoFrameMut {
let res = unsafe { ffw_frame_make_writable(self.ptr) };
if res < 0 {
panic!("unable to make the frame mutable");
}
let ptr = self.ptr;
self.ptr = ptr::null_mut();
VideoFrameMut {
ptr,
time_base: self.time_base,
}
}
}
impl Clone for VideoFrame {
fn clone(&self) -> Self {
let ptr = unsafe { ffw_frame_clone(self.ptr) };
if ptr.is_null() {
panic!("unable to clone a frame");
}
Self {
ptr,
time_base: self.time_base,
}
}
}
impl Drop for VideoFrame {
fn drop(&mut self) {
unsafe { ffw_frame_free(self.ptr) }
}
}
unsafe impl Send for VideoFrame {}
unsafe impl Sync for VideoFrame {}