use std::sync::{Arc, Mutex};
use std::time::Duration;
pub trait FrameSink: Send {
fn push_frame(&mut self, rgba: &[u8], width: u32, height: u32, pts: Duration);
fn flush(&mut self) {}
}
pub struct RgbaFrame {
pub data: Vec<u8>,
pub width: u32,
pub height: u32,
pub pts: Duration,
}
pub struct RgbaSink {
pub last_frame: Arc<Mutex<Option<RgbaFrame>>>,
}
impl RgbaSink {
#[must_use]
pub fn new() -> Self {
Self {
last_frame: Arc::new(Mutex::new(None)),
}
}
#[must_use]
pub fn frame_handle(&self) -> Arc<Mutex<Option<RgbaFrame>>> {
Arc::clone(&self.last_frame)
}
}
impl Default for RgbaSink {
fn default() -> Self {
Self::new()
}
}
impl FrameSink for RgbaSink {
fn push_frame(&mut self, rgba: &[u8], width: u32, height: u32, pts: Duration) {
let mut guard = self
.last_frame
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*guard = Some(RgbaFrame {
data: rgba.to_vec(),
width,
height,
pts,
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn frame_sink_should_be_object_safe() {
let _: Option<Box<dyn FrameSink>> = None;
}
#[test]
fn frame_sink_flush_default_should_be_a_noop() {
struct NoFlushSink;
impl FrameSink for NoFlushSink {
fn push_frame(&mut self, _rgba: &[u8], _width: u32, _height: u32, _pts: Duration) {}
}
let mut sink = NoFlushSink;
sink.flush(); }
#[test]
fn rgba_sink_should_store_latest_frame_on_push() {
let mut sink = RgbaSink::new();
let handle = sink.frame_handle();
assert!(
handle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_none(),
"frame_handle must be None before any push"
);
let rgba: Vec<u8> = vec![255u8, 0, 0, 255, 0, 255, 0, 255]; let pts = Duration::from_millis(100);
sink.push_frame(&rgba, 2, 1, pts);
let guard = handle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let frame = guard.as_ref().expect("frame must be Some after push");
assert_eq!(frame.width, 2);
assert_eq!(frame.height, 1);
assert_eq!(frame.pts, pts);
assert_eq!(frame.data, rgba);
}
#[test]
fn rgba_sink_should_replace_frame_on_second_push() {
let mut sink = RgbaSink::new();
let handle = sink.frame_handle();
let first: Vec<u8> = vec![1, 2, 3, 255];
let second: Vec<u8> = vec![9, 8, 7, 255];
let pts1 = Duration::from_millis(0);
let pts2 = Duration::from_millis(33);
sink.push_frame(&first, 1, 1, pts1);
sink.push_frame(&second, 1, 1, pts2);
let guard = handle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let frame = guard.as_ref().expect("frame must be Some after two pushes");
assert_eq!(
frame.data, second,
"latest push must overwrite previous frame"
);
assert_eq!(frame.pts, pts2);
}
#[test]
fn rgba_sink_default_should_equal_new() {
let a = RgbaSink::new();
let b = RgbaSink::default();
assert!(
a.frame_handle()
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_none()
);
assert!(
b.frame_handle()
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_none()
);
}
}