use std::sync::Arc;
use oximedia_audio::{AudioFrame, ChannelLayout};
use oximedia_codec::VideoFrame;
use oximedia_core::{PixelFormat, SampleFormat, Timestamp};
#[derive(Clone, Debug)]
pub enum FilterFrame {
Video(VideoFrame),
Audio(AudioFrame),
}
impl FilterFrame {
#[must_use]
pub fn timestamp(&self) -> &Timestamp {
match self {
Self::Video(f) => &f.timestamp,
Self::Audio(f) => &f.timestamp,
}
}
#[must_use]
pub fn is_video(&self) -> bool {
matches!(self, Self::Video(_))
}
#[must_use]
pub fn is_audio(&self) -> bool {
matches!(self, Self::Audio(_))
}
#[must_use]
pub fn as_video(&self) -> Option<&VideoFrame> {
match self {
Self::Video(f) => Some(f),
Self::Audio(_) => None,
}
}
#[must_use]
pub fn as_audio(&self) -> Option<&AudioFrame> {
match self {
Self::Video(_) => None,
Self::Audio(f) => Some(f),
}
}
pub fn as_video_mut(&mut self) -> Option<&mut VideoFrame> {
match self {
Self::Video(f) => Some(f),
Self::Audio(_) => None,
}
}
pub fn as_audio_mut(&mut self) -> Option<&mut AudioFrame> {
match self {
Self::Video(_) => None,
Self::Audio(f) => Some(f),
}
}
}
impl From<VideoFrame> for FilterFrame {
fn from(frame: VideoFrame) -> Self {
Self::Video(frame)
}
}
impl From<AudioFrame> for FilterFrame {
fn from(frame: AudioFrame) -> Self {
Self::Audio(frame)
}
}
#[derive(Clone, Debug)]
pub struct FrameRef {
inner: Arc<FilterFrame>,
}
impl FrameRef {
pub fn new(frame: FilterFrame) -> Self {
Self {
inner: Arc::new(frame),
}
}
#[must_use]
pub fn frame(&self) -> &FilterFrame {
&self.inner
}
pub fn try_unwrap(self) -> Option<FilterFrame> {
Arc::try_unwrap(self.inner).ok()
}
#[must_use]
pub fn ref_count(&self) -> usize {
Arc::strong_count(&self.inner)
}
#[must_use]
pub fn make_mut(self) -> FilterFrame {
match Arc::try_unwrap(self.inner) {
Ok(frame) => frame,
Err(arc) => (*arc).clone(),
}
}
}
impl From<FilterFrame> for FrameRef {
fn from(frame: FilterFrame) -> Self {
Self::new(frame)
}
}
impl From<VideoFrame> for FrameRef {
fn from(frame: VideoFrame) -> Self {
Self::new(FilterFrame::Video(frame))
}
}
impl From<AudioFrame> for FrameRef {
fn from(frame: AudioFrame) -> Self {
Self::new(FilterFrame::Audio(frame))
}
}
#[allow(dead_code)]
pub struct FramePool {
capacity: usize,
video_frames: Vec<VideoFrame>,
audio_frames: Vec<AudioFrame>,
}
impl FramePool {
#[must_use]
pub fn new(capacity: usize) -> Self {
Self {
capacity,
video_frames: Vec::with_capacity(capacity),
audio_frames: Vec::with_capacity(capacity),
}
}
#[must_use]
pub fn get_video_frame(&mut self, format: PixelFormat, width: u32, height: u32) -> VideoFrame {
if let Some(pos) = self
.video_frames
.iter()
.position(|f| f.format == format && f.width == width && f.height == height)
{
return self.video_frames.swap_remove(pos);
}
let mut frame = VideoFrame::new(format, width, height);
frame.allocate();
frame
}
pub fn return_video_frame(&mut self, frame: VideoFrame) {
if self.video_frames.len() < self.capacity {
self.video_frames.push(frame);
}
}
#[must_use]
pub fn get_audio_frame(
&mut self,
format: SampleFormat,
sample_rate: u32,
channels: ChannelLayout,
) -> AudioFrame {
if let Some(pos) = self.audio_frames.iter().position(|f| {
f.format == format && f.sample_rate == sample_rate && f.channels == channels
}) {
return self.audio_frames.swap_remove(pos);
}
AudioFrame::new(format, sample_rate, channels)
}
pub fn return_audio_frame(&mut self, frame: AudioFrame) {
if self.audio_frames.len() < self.capacity {
self.audio_frames.push(frame);
}
}
pub fn clear(&mut self) {
self.video_frames.clear();
self.audio_frames.clear();
}
#[must_use]
pub fn video_frame_count(&self) -> usize {
self.video_frames.len()
}
#[must_use]
pub fn audio_frame_count(&self) -> usize {
self.audio_frames.len()
}
}
impl Default for FramePool {
fn default() -> Self {
Self::new(16)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_filter_frame_video() {
let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
let frame = FilterFrame::Video(video);
assert!(frame.is_video());
assert!(!frame.is_audio());
assert!(frame.as_video().is_some());
assert!(frame.as_audio().is_none());
}
#[test]
fn test_filter_frame_audio() {
let audio = AudioFrame::new(SampleFormat::F32, 48000, ChannelLayout::Stereo);
let frame = FilterFrame::Audio(audio);
assert!(!frame.is_video());
assert!(frame.is_audio());
assert!(frame.as_video().is_none());
assert!(frame.as_audio().is_some());
}
#[test]
fn test_frame_ref() {
let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
let frame = FilterFrame::Video(video);
let frame_ref = FrameRef::new(frame);
assert_eq!(frame_ref.ref_count(), 1);
let frame_ref2 = frame_ref.clone();
assert_eq!(frame_ref.ref_count(), 2);
assert_eq!(frame_ref2.ref_count(), 2);
}
#[test]
fn test_frame_ref_try_unwrap() {
let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
let frame = FilterFrame::Video(video);
let frame_ref = FrameRef::new(frame);
let unwrapped = frame_ref.try_unwrap();
assert!(unwrapped.is_some());
}
#[test]
fn test_frame_ref_make_mut() {
let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
let frame = FilterFrame::Video(video);
let frame_ref = FrameRef::new(frame);
let frame_ref2 = frame_ref.clone();
let owned = frame_ref.make_mut();
assert!(owned.is_video());
assert!(frame_ref2.frame().is_video());
}
#[test]
fn test_frame_pool() {
let mut pool = FramePool::new(4);
let frame = pool.get_video_frame(PixelFormat::Yuv420p, 1920, 1080);
assert_eq!(frame.width, 1920);
assert_eq!(frame.height, 1080);
pool.return_video_frame(frame);
assert_eq!(pool.video_frame_count(), 1);
let frame2 = pool.get_video_frame(PixelFormat::Yuv420p, 1920, 1080);
assert_eq!(frame2.width, 1920);
assert_eq!(pool.video_frame_count(), 0);
}
#[test]
fn test_filter_frame_from() {
let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
let frame: FilterFrame = video.into();
assert!(frame.is_video());
let audio = AudioFrame::new(SampleFormat::F32, 48000, ChannelLayout::Stereo);
let frame: FilterFrame = audio.into();
assert!(frame.is_audio());
}
#[test]
fn test_frame_timestamp() {
use oximedia_core::Rational;
let mut video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
video.timestamp = Timestamp::new(1000, Rational::new(1, 1000));
let frame = FilterFrame::Video(video);
assert_eq!(frame.timestamp().pts, 1000);
}
}