use std::sync::Arc;
use crate::Error;
use crate::frame::Frame;
mod channel;
use channel::FrameChannel;
#[cfg(not(target_os = "macos"))]
type Keepalive = Box<dyn std::any::Any + Send>;
#[cfg(target_os = "macos")]
type Keepalive = Box<dyn std::any::Any>;
#[cfg(target_os = "macos")]
mod avfoundation;
#[cfg(target_os = "macos")]
mod screencapture;
#[cfg(target_os = "macos")]
mod surface;
#[cfg(target_os = "linux")]
mod v4l2;
#[cfg(all(target_os = "linux", feature = "pipewire"))]
mod pipewire;
#[cfg(target_os = "windows")]
mod mediafoundation;
#[cfg(target_os = "windows")]
mod desktopduplication;
#[cfg(any(target_os = "linux", target_os = "windows"))]
mod pump;
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Source {
Camera(Option<String>),
Display(Option<String>),
Window(String),
App(String),
}
impl Default for Source {
fn default() -> Self {
Self::Camera(None)
}
}
impl Source {
#[cfg(target_os = "macos")]
pub(crate) fn label(&self) -> String {
match self {
Self::Camera(None) => "camera".to_string(),
Self::Camera(Some(id)) => format!("camera:{id}"),
Self::Display(None) => "display".to_string(),
Self::Display(Some(id)) => format!("display:{id}"),
Self::Window(id) => format!("window:{id}"),
Self::App(id) => format!("app:{id}"),
}
}
}
#[derive(Clone, Debug)]
pub struct Camera {
pub id: String,
pub name: String,
}
impl Camera {
pub fn source(&self) -> Source {
Source::Camera(Some(self.id.clone()))
}
}
#[derive(Clone, Debug)]
pub struct Display {
pub id: String,
pub name: String,
pub width: u32,
pub height: u32,
}
impl Display {
pub fn source(&self) -> Source {
Source::Display(Some(self.id.clone()))
}
}
#[derive(Clone, Debug)]
pub struct Window {
pub id: String,
pub title: String,
pub app: String,
pub width: u32,
pub height: u32,
}
impl Window {
pub fn source(&self) -> Source {
Source::Window(self.id.clone())
}
}
#[derive(Clone, Debug)]
pub struct App {
pub id: String,
pub name: String,
}
impl App {
pub fn source(&self) -> Source {
Source::App(self.id.clone())
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Config {
pub source: Source,
pub width: Option<u32>,
pub height: Option<u32>,
pub framerate: Option<u32>,
pub cursor: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
source: Source::default(),
width: None,
height: None,
framerate: None,
cursor: true,
}
}
}
pub(crate) struct FrameStream {
chan: Arc<FrameChannel>,
width: u32,
height: u32,
framerate: Option<u32>,
device: String,
pending: Option<Frame>,
_backend: Keepalive,
}
impl FrameStream {
fn new(
chan: Arc<FrameChannel>,
width: u32,
height: u32,
framerate: Option<u32>,
device: String,
pending: Option<Frame>,
backend: Keepalive,
) -> Self {
Self {
chan,
width,
height,
framerate,
device,
pending,
_backend: backend,
}
}
pub(crate) async fn read(&mut self) -> Option<Frame> {
if let Some(frame) = self.pending.take() {
return Some(frame);
}
self.chan.recv().await
}
pub(crate) fn width(&self) -> u32 {
self.width
}
pub(crate) fn height(&self) -> u32 {
self.height
}
pub(crate) fn framerate(&self) -> Option<u32> {
self.framerate
}
pub(crate) fn device(&self) -> &str {
&self.device
}
}
pub(crate) async fn open(config: &Config) -> Result<FrameStream, Error> {
match &config.source {
Source::Camera(device) => {
let _ = device;
#[cfg(target_os = "macos")]
{
avfoundation::open(config, device.as_deref()).await
}
#[cfg(target_os = "linux")]
{
v4l2::open(config, device.as_deref()).await
}
#[cfg(target_os = "windows")]
{
mediafoundation::open(config, device.as_deref()).await
}
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
{
Err(Error::Unsupported("camera capture".to_string()))
}
}
Source::Display(device) => {
let _ = device;
#[cfg(target_os = "macos")]
{
screencapture::open_display(config, device.as_deref()).await
}
#[cfg(target_os = "windows")]
{
desktopduplication::open(config, device.as_deref()).await
}
#[cfg(all(target_os = "linux", feature = "pipewire"))]
{
pipewire::open(config, device.as_deref()).await
}
#[cfg(all(target_os = "linux", not(feature = "pipewire")))]
{
Err(Error::Unsupported(
"screen capture on Linux without the `pipewire` feature".to_string(),
))
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
{
Err(Error::Unsupported("screen capture".to_string()))
}
}
Source::Window(id) => {
let _ = id;
#[cfg(target_os = "macos")]
{
screencapture::open_window(config, id).await
}
#[cfg(not(target_os = "macos"))]
{
Err(Error::Unsupported("window capture".to_string()))
}
}
Source::App(id) => {
let _ = id;
#[cfg(target_os = "macos")]
{
screencapture::open_app(config, id).await
}
#[cfg(not(target_os = "macos"))]
{
Err(Error::Unsupported("application capture".to_string()))
}
}
}
}
pub async fn cameras() -> Result<Vec<Camera>, Error> {
#[cfg(target_os = "macos")]
{
avfoundation::cameras()
}
#[cfg(not(target_os = "macos"))]
{
Err(Error::Unsupported("listing cameras".to_string()))
}
}
pub async fn displays() -> Result<Vec<Display>, Error> {
#[cfg(target_os = "macos")]
{
screencapture::displays().await
}
#[cfg(not(target_os = "macos"))]
{
Err(Error::Unsupported("listing displays".to_string()))
}
}
pub async fn windows() -> Result<Vec<Window>, Error> {
#[cfg(target_os = "macos")]
{
screencapture::windows().await
}
#[cfg(not(target_os = "macos"))]
{
Err(Error::Unsupported("listing windows".to_string()))
}
}
pub async fn apps() -> Result<Vec<App>, Error> {
#[cfg(target_os = "macos")]
{
screencapture::apps().await
}
#[cfg(not(target_os = "macos"))]
{
Err(Error::Unsupported("listing applications".to_string()))
}
}