use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
pub type CaptureFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CaptureSessionId(pub u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TrackId(pub u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TrackKind {
Visual,
SystemAudio,
Microphone,
}
impl TrackKind {
pub const ALL: [TrackKind; 3] = [
TrackKind::Visual,
TrackKind::SystemAudio,
TrackKind::Microphone,
];
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CaptureCapabilities {
pub combinations: Vec<TrackSet>,
}
impl CaptureCapabilities {
pub fn visual_only() -> Self {
Self {
combinations: vec![TrackSet {
tracks: vec![TrackKind::Visual],
coupled_clock: false,
}],
}
}
pub fn supports(&self, kind: TrackKind) -> bool {
self.combinations
.iter()
.any(|set| set.tracks.contains(&kind))
}
pub fn supports_set(&self, kinds: &[TrackKind]) -> Option<&TrackSet> {
self.combinations.iter().find(|set| {
kinds.iter().all(|kind| set.tracks.contains(kind)) && set.tracks.len() == kinds.len()
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TrackSet {
pub tracks: Vec<TrackKind>,
pub coupled_clock: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VisualTarget {
Screen,
Display {
id: String,
},
Window {
id: String,
},
Region {
x: i32,
y: i32,
width: u32,
height: u32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Size {
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rect {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
}
impl Rect {
pub fn contains(self, x: i32, y: i32) -> bool {
let w = self.width.max(0);
let h = self.height.max(0);
x >= self.x && y >= self.y && x < self.x.saturating_add(w) && y < self.y.saturating_add(h)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct VisualGeometry {
pub session_id: CaptureSessionId,
pub generation: u64,
pub source: Rect,
pub target_local: Rect,
pub output: Size,
pub content_in_output: Rect,
pub scale: f64,
pub rotation_degrees: i32,
pub mirrored: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VideoCodec {
H264,
Hevc,
Vp8,
Vp9,
Av1,
Png,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AudioCodec {
Opus,
Aac,
PcmS16le,
}
#[derive(Debug, Clone, PartialEq)]
pub struct VisualTrackConfig {
pub target: VisualTarget,
pub max_size: Option<Size>,
pub fps: Option<u32>,
pub bitrate_kbps: Option<u32>,
pub codec: Option<VideoCodec>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AudioTrackConfig {
pub sample_rate: Option<u32>,
pub channels: Option<u8>,
pub bitrate_kbps: Option<u32>,
pub codec: Option<AudioCodec>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TrackRequest {
pub kind: TrackKind,
pub required: bool,
pub visual: Option<VisualTrackConfig>,
pub audio: Option<AudioTrackConfig>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProviderCaptureRequest {
pub tracks: Vec<TrackRequest>,
pub authorization: Option<CaptureAuthorization>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CaptureAuthorization {
pub kind: AuthorizationKind,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthorizationKind {
AndroidMediaProjection,
AppleScreenRecording,
HarmonyScreenCapture,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CaptureTimestamp {
pub monotonic_nanos: u64,
pub wall_unix_nanos: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncodedPacket {
pub track_id: TrackId,
pub kind: TrackKind,
pub timestamp: CaptureTimestamp,
pub sequence: u64,
pub format_generation: u64,
pub geometry_generation: Option<u64>,
pub keyframe: bool,
pub video_codec: Option<VideoCodec>,
pub audio_codec: Option<AudioCodec>,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiscontinuityReason {
Overflow,
Resynchronize,
Gap,
Reconfigure,
}
#[derive(Debug, Clone)]
pub enum ProviderEvent {
TrackConfigured {
track_id: TrackId,
kind: TrackKind,
format_generation: u64,
video_codec: Option<VideoCodec>,
audio_codec: Option<AudioCodec>,
sample_rate: Option<u32>,
channels: Option<u8>,
size: Option<Size>,
},
VisualGeometry {
track_id: TrackId,
geometry: VisualGeometry,
},
EncodedPacket(EncodedPacket),
Discontinuity {
track_id: TrackId,
kind: TrackKind,
reason: DiscontinuityReason,
format_generation: u64,
timestamp: CaptureTimestamp,
},
AuthorizationRevoked {
track: TrackKind,
},
Failed {
track: Option<TrackKind>,
error: CaptureError,
},
Stopped,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CaptureError {
Composition(String),
AuthorizationRequired {
track: TrackKind,
},
Denied {
track: TrackKind,
},
Unavailable {
track: Option<TrackKind>,
reason: String,
},
Failed {
reason: String,
},
InvalidRequest(String),
StaleGeneration,
UnknownSession,
}
impl fmt::Display for CaptureError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Composition(reason) => write!(f, "capture composition: {reason}"),
Self::AuthorizationRequired { track } => {
write!(f, "authorization required for {track:?}")
}
Self::Denied { track } => write!(f, "capture denied for {track:?}"),
Self::Unavailable { track, reason } => match track {
Some(track) => write!(f, "capture unavailable for {track:?}: {reason}"),
None => write!(f, "capture unavailable: {reason}"),
},
Self::Failed { reason } => write!(f, "capture failed: {reason}"),
Self::InvalidRequest(reason) => write!(f, "invalid capture request: {reason}"),
Self::StaleGeneration => write!(f, "stale capture geometry generation"),
Self::UnknownSession => write!(f, "unknown capture session"),
}
}
}
impl std::error::Error for CaptureError {}
pub trait MediaCaptureProvider: Send + Sync {
fn id(&self) -> &'static str;
fn capabilities(&self) -> CaptureCapabilities;
fn start(
&self,
request: ProviderCaptureRequest,
events: Arc<dyn ProviderEventSink>,
) -> CaptureFuture<Result<Box<dyn ProviderCaptureSession>, CaptureError>>;
}
pub trait ProviderCaptureSession: Send + Sync {
fn reconfigure(
&self,
request: ProviderCaptureRequest,
) -> CaptureFuture<Result<(), CaptureError>>;
fn request_keyframe(&self, track: TrackId) -> Result<(), CaptureError>;
fn suspend(&self) -> Result<(), CaptureError>;
fn resume(&self) -> Result<(), CaptureError>;
fn stop(&self);
}
pub trait ProviderEventSink: Send + Sync {
fn emit(&self, event: ProviderEvent);
}
#[cfg(all(feature = "android-capture-provider", target_os = "android"))]
pub mod android;
#[cfg(all(
feature = "apple-capture-provider",
any(target_os = "ios", target_os = "macos")
))]
pub mod apple;
#[cfg(all(feature = "harmony-capture-provider", target_env = "ohos"))]
pub mod harmony;