use moq_net::Timestamp;
use crate::{Error, Size, Surface};
pub(crate) mod backend;
mod consumer;
mod decoder;
pub use consumer::Consumer;
pub use decoder::{Config, Decoder, Kind};
pub struct Frame {
pub timestamp: Timestamp,
pub size: Size,
pub surface: Surface,
}
impl Frame {
pub fn resize(&self, size: Size) -> Result<Frame, Error> {
size.validate("resize to")?;
let Size { width, height } = size;
let surface = match &self.surface {
Surface::I420(i420) => Surface::I420(i420.resize(width, height)?),
#[cfg(all(target_os = "linux", feature = "nvdec"))]
Surface::Cuda(cuda) => match cuda.resize(width, height) {
Ok(scaled) => Surface::Cuda(scaled),
Err(err) => {
static WARN_ONCE: std::sync::Once = std::sync::Once::new();
WARN_ONCE.call_once(|| tracing::warn!(%err, "GPU resize failed; falling back to the CPU"));
Surface::I420(cuda.download_i420()?.resize(width, height)?)
}
},
#[allow(unreachable_patterns)]
other => Surface::I420(other.to_i420()?.into_owned().resize(width, height)?),
};
Ok(Frame {
timestamp: self.timestamp,
size,
surface,
})
}
}
#[cfg(test)]
mod tests {
#[test]
fn frame_and_consumer_are_thread_safe() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<super::Frame>();
assert_sync::<super::Frame>();
assert_send::<super::Consumer>();
}
#[cfg(target_os = "macos")]
#[test]
fn into_pixel_buffer_uploads_a_cpu_frame() {
use objc2_core_video::{CVPixelBufferGetHeight, CVPixelBufferGetWidth};
let frame = super::Frame {
timestamp: moq_net::Timestamp::from_micros(0).unwrap(),
size: crate::Size::new(64, 32),
surface: crate::Surface::I420(crate::I420 {
width: 64,
height: 32,
data: vec![0x80; crate::I420::len(64, 32)],
}),
};
let buffer = frame.surface.into_pixel_buffer().expect("upload a CPU frame");
assert_eq!(CVPixelBufferGetWidth(&buffer), 64);
assert_eq!(CVPixelBufferGetHeight(&buffer), 32);
}
}