use std::sync::Mutex;
#[cfg(not(target_os = "macos"))]
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::ThreadId;
use bytes::Bytes;
use moq_net::Timestamp;
use super::{Backend, Codec, Config};
use crate::{Error, Frame, I420, Size, Surface};
pub(crate) const NAME: &str = "probe";
pub(crate) const BUFFERED_NAME: &str = "probe-buffered";
#[cfg(not(target_os = "macos"))]
pub(crate) const BLOCKING_FLUSH_NAME: &str = "probe-blocking-flush";
pub(crate) type Event = (&'static str, ThreadId);
static LOG: Mutex<Vec<Event>> = Mutex::new(Vec::new());
#[cfg(not(target_os = "macos"))]
static FLUSH_ENTERED: AtomicBool = AtomicBool::new(false);
#[cfg(not(target_os = "macos"))]
static FLUSH_RELEASED: AtomicBool = AtomicBool::new(false);
#[cfg(not(target_os = "macos"))]
static EXCLUSIVE: Mutex<()> = Mutex::new(());
#[cfg(not(target_os = "macos"))]
pub(crate) fn exclusive() -> std::sync::MutexGuard<'static, ()> {
let guard = EXCLUSIVE.lock().unwrap_or_else(|err| err.into_inner());
let _ = take();
guard
}
#[cfg(not(target_os = "macos"))]
pub(crate) fn take() -> Vec<Event> {
std::mem::take(&mut LOG.lock().unwrap())
}
#[cfg(not(target_os = "macos"))]
pub(crate) fn prepare_blocking_flush() {
FLUSH_ENTERED.store(false, Ordering::SeqCst);
FLUSH_RELEASED.store(false, Ordering::SeqCst);
}
#[cfg(not(target_os = "macos"))]
pub(crate) fn flush_entered() -> bool {
FLUSH_ENTERED.load(Ordering::SeqCst)
}
#[cfg(not(target_os = "macos"))]
pub(crate) fn release_flush() {
FLUSH_RELEASED.store(true, Ordering::SeqCst);
}
fn record(what: &'static str) {
LOG.lock().unwrap().push((what, std::thread::current().id()));
}
pub(crate) const SIZE: Size = Size {
width: 320,
height: 240,
};
pub(crate) struct Probe;
pub(crate) struct Buffered(Option<Frame>);
#[cfg(not(target_os = "macos"))]
pub(crate) struct BlockingFlush;
impl Probe {
pub(crate) fn open(_codec: Codec, _config: &Config) -> Result<Box<dyn Backend>, Error> {
record("open");
Ok(Box::new(Self))
}
}
impl Buffered {
pub(crate) fn open(_codec: Codec, _config: &Config) -> Result<Box<dyn Backend>, Error> {
Ok(Box::new(Self(None)))
}
}
#[cfg(not(target_os = "macos"))]
impl BlockingFlush {
pub(crate) fn open(_codec: Codec, _config: &Config) -> Result<Box<dyn Backend>, Error> {
Ok(Box::new(Self))
}
}
fn frame(timestamp: Timestamp) -> Result<Frame, Error> {
let i420 = I420::new(
SIZE.width,
SIZE.height,
vec![0x80u8; I420::len(SIZE.width, SIZE.height)],
)?;
Ok(Frame::new(Surface::I420(i420), timestamp))
}
impl Backend for Probe {
fn decode(&mut self, _access_unit: Bytes, timestamp: Timestamp, _keyframe: bool) -> Result<Vec<Frame>, Error> {
record("decode");
Ok(vec![frame(timestamp)?])
}
fn flush(&mut self) -> Result<Vec<Frame>, Error> {
Ok(Vec::new())
}
fn name(&self) -> &str {
NAME
}
}
#[cfg(not(target_os = "macos"))]
impl Backend for BlockingFlush {
fn decode(&mut self, _access_unit: Bytes, _timestamp: Timestamp, _keyframe: bool) -> Result<Vec<Frame>, Error> {
Ok(Vec::new())
}
fn flush(&mut self) -> Result<Vec<Frame>, Error> {
FLUSH_ENTERED.store(true, Ordering::SeqCst);
while !FLUSH_RELEASED.load(Ordering::SeqCst) {
std::thread::yield_now();
}
Ok(Vec::new())
}
fn name(&self) -> &str {
BLOCKING_FLUSH_NAME
}
}
impl Backend for Buffered {
fn decode(&mut self, _access_unit: Bytes, timestamp: Timestamp, _keyframe: bool) -> Result<Vec<Frame>, Error> {
Ok(self.0.replace(frame(timestamp)?).into_iter().collect())
}
fn flush(&mut self) -> Result<Vec<Frame>, Error> {
Ok(self.0.take().into_iter().collect())
}
fn name(&self) -> &str {
BUFFERED_NAME
}
}
impl Drop for Probe {
fn drop(&mut self) {
record("drop");
}
}