use mediaway_common::CodecKind;
use mediaway_decoder::capability::{DecodeSupport, DecodeUnavailable};
use mediaway_decoder::{DecodeError, VideoDecoder, VideoDecoderConfig};
use mediaway_device::audio::{AudioCapture, AudioCaptureConfig};
use mediaway_device::desktop::{
DesktopAudioCaptureConfig, DesktopVideoCapture, DesktopVideoCaptureConfig,
};
use mediaway_device::{CaptureError, DeviceKind, PermissionState, Support};
use mediaway_encoder::auto::AutoVideoEncodeConfig;
use mediaway_encoder::capability::EncoderCapability;
use mediaway_encoder::{EncodeError, VideoEncoder};
pub struct AutoEncoder;
impl AutoEncoder {
pub fn open(config: &AutoVideoEncodeConfig) -> Result<Box<dyn VideoEncoder>, EncodeError> {
#[cfg(windows)]
{
use mediaway_encoder::windows::auto::AutoVideoEncoder;
let enc = AutoVideoEncoder::open(config)?;
Ok(Box::new(enc))
}
#[cfg(target_os = "linux")]
{
use mediaway_encoder::VideoInputPreference;
use mediaway_encoder::linux::LinuxVideoEncoder;
if config.gpu_device.is_some() {
let low = config.to_low_level(VideoInputPreference::ZeroCopyGpu, config.gpu_device);
if let Ok(enc) = LinuxVideoEncoder::open(&low) {
return Ok(Box::new(enc));
}
}
let low = config.to_low_level(VideoInputPreference::CpuUploadOk, config.gpu_device);
let enc = LinuxVideoEncoder::open(&low)?;
Ok(Box::new(enc))
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
use mediaway_encoder::VideoInputPreference;
use mediaway_encoder::apple::AppleVideoEncoder;
if config.gpu_device.is_some() {
let low = config.to_low_level(VideoInputPreference::ZeroCopyGpu, config.gpu_device);
if let Ok(enc) = AppleVideoEncoder::open(&low) {
return Ok(Box::new(enc));
}
}
let low = config.to_low_level(VideoInputPreference::CpuUploadOk, config.gpu_device);
let enc = AppleVideoEncoder::open(&low)?;
Ok(Box::new(enc))
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos", target_os = "ios")))]
{
let _ = config;
Err(EncodeError::NoBackend)
}
}
}
#[must_use]
pub fn encoder_support(codec: CodecKind) -> Vec<EncoderCapability> {
encoder_support_at(
codec,
mediaway_encoder::capability::DEFAULT_PROBE_WIDTH,
mediaway_encoder::capability::DEFAULT_PROBE_HEIGHT,
)
}
#[must_use]
#[allow(
clippy::missing_const_for_fn,
reason = "the Windows branch calls a non-const backend probe; only the other-platforms \
branch (compiled alone on non-Windows) is trivially const-eligible"
)]
pub fn encoder_support_at(codec: CodecKind, width: u32, height: u32) -> Vec<EncoderCapability> {
#[cfg(windows)]
{
if codec == CodecKind::Opus {
use mediaway_common::{Rational, SampleFormat};
use mediaway_encoder::auto::{Backend, EncodePathClass};
use mediaway_encoder::capability::{EncodeSupport, EncodeUnavailable};
use mediaway_encoder::{AudioEncoderConfig, EncodeError, SwOpusAudioEncoder};
let cfg = AudioEncoderConfig {
codec,
sample_rate: 48_000,
channels: 2,
sample_format: SampleFormat::F32,
time_base: Rational::new(1, 50),
bitrate_bps: 0,
};
let support = match SwOpusAudioEncoder::open(&cfg) {
Ok(_) => EncodeSupport::Supported(EncodePathClass::Software),
Err(EncodeError::Backend) => {
EncodeSupport::Unavailable(EncodeUnavailable::NoDevice)
}
Err(_) => EncodeSupport::Unavailable(EncodeUnavailable::NotImplemented),
};
return vec![EncoderCapability::new(Backend::Software, support)];
}
mediaway_encoder::windows::auto::support_at(codec, width, height)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
if codec == CodecKind::Opus {
use mediaway_common::{Rational, SampleFormat};
use mediaway_encoder::apple::AppleAudioEncoder;
use mediaway_encoder::auto::{Backend, EncodePathClass};
use mediaway_encoder::capability::{EncodeSupport, EncodeUnavailable};
use mediaway_encoder::{AudioEncoderConfig, EncodeError};
let cfg = AudioEncoderConfig {
codec,
sample_rate: 48_000,
channels: 2,
sample_format: SampleFormat::F32,
time_base: Rational::new(1, 50),
bitrate_bps: 0,
};
let support = match AppleAudioEncoder::open(&cfg) {
Ok(_) => EncodeSupport::Supported(EncodePathClass::CpuUpload),
Err(EncodeError::Backend) => {
EncodeSupport::Unavailable(EncodeUnavailable::NoDevice)
}
Err(_) => EncodeSupport::Unavailable(EncodeUnavailable::NotImplemented),
};
return vec![EncoderCapability::new(Backend::Os, support)];
}
let _ = (codec, width, height);
Vec::new()
}
#[cfg(not(any(windows, target_os = "macos", target_os = "ios")))]
{
let _ = (codec, width, height);
Vec::new()
}
}
pub struct AutoDecoder;
impl AutoDecoder {
pub fn open(config: &VideoDecoderConfig) -> Result<Box<dyn VideoDecoder>, DecodeError> {
#[cfg(windows)]
{
use mediaway_decoder::windows::WindowsVideoDecoder;
let dec = WindowsVideoDecoder::open(config)?;
Ok(Box::new(dec))
}
#[cfg(target_os = "linux")]
{
use mediaway_decoder::linux::LinuxVideoDecoder;
let dec = LinuxVideoDecoder::open(config)?;
Ok(Box::new(dec))
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
use mediaway_decoder::apple::AppleVideoDecoder;
let dec = AppleVideoDecoder::open(config)?;
Ok(Box::new(dec))
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos", target_os = "ios")))]
{
let _ = config;
Err(DecodeError::NoBackend)
}
}
}
#[must_use]
pub fn decoder_support(codec: CodecKind) -> DecodeSupport {
#[cfg(windows)]
{
use mediaway_decoder::windows::{
AacDecoderConfig, OpusDecoderConfig, WindowsVideoDecoder, WmfAacDecoder, WmfOpusDecoder,
};
if codec == CodecKind::Opus {
return match WmfOpusDecoder::open(&OpusDecoderConfig::new(48_000, 2)) {
Ok(_) => DecodeSupport::Supported,
Err(_) => DecodeSupport::Unavailable(DecodeUnavailable::NoDevice),
};
}
if codec == CodecKind::Aac {
const PROBE_ASC: [u8; 2] = [0x12, 0x10];
let cfg =
AacDecoderConfig::new(44_100, 2, mediaway_common::Bytes::from(PROBE_ASC.to_vec()));
return match WmfAacDecoder::open(&cfg) {
Ok(_) => DecodeSupport::Supported,
Err(_) => DecodeSupport::Unavailable(DecodeUnavailable::NoDevice),
};
}
let cfg = VideoDecoderConfig {
codec,
output: mediaway_decoder::VideoOutputPreference::CpuFramesOk,
..VideoDecoderConfig::h264(64, 64, mediaway_common::Rational::new(1, 30))
};
match WindowsVideoDecoder::open(&cfg) {
Ok(_) => DecodeSupport::Supported,
Err(DecodeError::Unsupported) => {
DecodeSupport::Unavailable(DecodeUnavailable::NotImplemented)
}
Err(_) => DecodeSupport::Unavailable(DecodeUnavailable::NoDevice),
}
}
#[cfg(target_os = "linux")]
{
use mediaway_decoder::linux::LinuxVideoDecoder;
let cfg = VideoDecoderConfig {
codec,
output: mediaway_decoder::VideoOutputPreference::CpuFramesOk,
..VideoDecoderConfig::h264(64, 64, mediaway_common::Rational::new(1, 30))
};
match LinuxVideoDecoder::open(&cfg) {
Ok(_) => DecodeSupport::Supported,
Err(DecodeError::Unsupported) => {
DecodeSupport::Unavailable(DecodeUnavailable::NotImplemented)
}
Err(_) => DecodeSupport::Unavailable(DecodeUnavailable::NoDevice),
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
use mediaway_decoder::apple::AppleVideoDecoder;
if codec == CodecKind::Opus {
use mediaway_decoder::apple::{OpusDecoder, OpusDecoderConfig};
let cfg = OpusDecoderConfig::new(48_000, 2, mediaway_common::Rational::new(1, 50));
return match OpusDecoder::open(&cfg) {
Ok(_) => DecodeSupport::Supported,
Err(_) => DecodeSupport::Unavailable(DecodeUnavailable::NoDevice),
};
}
if codec == CodecKind::Aac {
use mediaway_decoder::apple::{AacDecoder, AacDecoderConfig};
const PROBE_ASC: [u8; 2] = [0x12, 0x10];
let cfg = AacDecoderConfig::new(
44_100,
2,
mediaway_common::Rational::new(1, 44_100),
mediaway_common::Bytes::from(PROBE_ASC.to_vec()),
);
return match AacDecoder::open(&cfg) {
Ok(_) => DecodeSupport::Supported,
Err(_) => DecodeSupport::Unavailable(DecodeUnavailable::NoDevice),
};
}
if matches!(
codec,
mediaway_common::CodecKind::Vp9 | mediaway_common::CodecKind::Av1
) {
return DecodeSupport::Unavailable(DecodeUnavailable::NotImplemented);
}
let cfg = VideoDecoderConfig {
codec,
output: mediaway_decoder::VideoOutputPreference::CpuFramesOk,
..VideoDecoderConfig::h264(64, 64, mediaway_common::Rational::new(1, 30))
};
match AppleVideoDecoder::open(&cfg) {
Ok(_) => DecodeSupport::Supported,
Err(DecodeError::Unsupported) => {
DecodeSupport::Unavailable(DecodeUnavailable::NotImplemented)
}
Err(_) => DecodeSupport::Unavailable(DecodeUnavailable::NoDevice),
}
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos", target_os = "ios")))]
{
let _ = codec;
DecodeSupport::Unavailable(DecodeUnavailable::NotImplemented)
}
}
pub struct ScreenCapture;
impl ScreenCapture {
pub fn open(
config: &DesktopVideoCaptureConfig,
) -> Result<Box<dyn DesktopVideoCapture>, CaptureError> {
#[cfg(windows)]
{
use mediaway_device::windows_desktop::WindowsScreenCapture;
let cap = WindowsScreenCapture::open(config)?;
Ok(Box::new(cap))
}
#[cfg(target_os = "linux")]
{
use mediaway_device::linux::LinuxScreenCapture;
let cap = LinuxScreenCapture::open(config)?;
Ok(Box::new(cap))
}
#[cfg(target_os = "macos")]
{
use mediaway_device::apple::AppleScreenCapture;
let cap = AppleScreenCapture::open(config)?;
Ok(Box::new(cap))
}
#[cfg(target_os = "ios")]
{
use mediaway_device::apple::AppleScreenCapture;
if config.cursor == mediaway_device::desktop::CursorCapture::Included
|| config.region.is_some()
{
return Err(CaptureError::Unsupported);
}
let cap = AppleScreenCapture::open()?;
Ok(Box::new(cap))
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos", target_os = "ios")))]
{
let _ = config;
Err(CaptureError::NoBackend)
}
}
}
#[cfg(windows)]
pub type PlatformWindowCapture = mediaway_device::windows_desktop::WindowsWindowCapture;
#[cfg(target_os = "linux")]
pub type PlatformWindowCapture = mediaway_device::linux::LinuxWindowCapture;
#[cfg(target_os = "macos")]
pub type PlatformWindowCapture = mediaway_device::apple::AppleWindowCapture;
#[cfg(not(any(windows, target_os = "linux", target_os = "macos")))]
pub type PlatformWindowCapture = core::convert::Infallible;
pub struct WindowCapture;
impl WindowCapture {
#[allow(
clippy::missing_const_for_fn,
reason = "the desktop branches open a capture session; only the no-backend branch, \
compiled alone on other targets, is trivially const-eligible"
)]
pub fn open(config: &DesktopVideoCaptureConfig) -> Result<PlatformWindowCapture, CaptureError> {
#[cfg(windows)]
{
mediaway_device::windows_desktop::WindowsWindowCapture::open(config)
}
#[cfg(target_os = "linux")]
{
mediaway_device::linux::LinuxWindowCapture::open(config)
}
#[cfg(target_os = "macos")]
{
mediaway_device::apple::AppleWindowCapture::open(config)
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos")))]
{
let _ = config;
Err(CaptureError::NoBackend)
}
}
}
#[cfg(windows)]
pub type PlatformDesktopAudioCapture = mediaway_device::windows_desktop::WindowsDesktopAudioCapture;
#[cfg(not(windows))]
pub type PlatformDesktopAudioCapture = core::convert::Infallible;
pub struct DesktopAudio;
impl DesktopAudio {
#[allow(
clippy::missing_const_for_fn,
reason = "the Windows branch opens a WASAPI session; only the no-backend branch, compiled \
alone elsewhere, is trivially const-eligible"
)]
pub fn open(
config: &DesktopAudioCaptureConfig,
) -> Result<PlatformDesktopAudioCapture, CaptureError> {
#[cfg(windows)]
{
mediaway_device::windows_desktop::WindowsDesktopAudioCapture::open(config)
}
#[cfg(not(windows))]
{
let _ = config;
Err(CaptureError::NoBackend)
}
}
}
pub struct Microphone;
impl Microphone {
pub fn open(config: &AudioCaptureConfig) -> Result<Box<dyn AudioCapture>, CaptureError> {
#[cfg(windows)]
{
use mediaway_device::windows_audio::WindowsWasapiCapture;
let cap = WindowsWasapiCapture::open_microphone(config)?;
Ok(Box::new(cap))
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
use mediaway_device::apple::AppleMicrophoneCapture;
let cap = AppleMicrophoneCapture::open(config)?;
Ok(Box::new(cap))
}
#[cfg(not(any(windows, target_os = "macos", target_os = "ios")))]
{
let _ = config;
Err(CaptureError::NoBackend)
}
}
}
#[must_use]
pub fn device_support(kind: DeviceKind) -> Support {
#[cfg(windows)]
{
mediaway_device::windows::support(kind)
}
#[cfg(target_os = "linux")]
{
mediaway_device::linux::support(kind)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
mediaway_device::apple::support(kind)
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos", target_os = "ios")))]
{
let _ = kind;
Support::Unavailable(mediaway_device::Unavailable::NotImplemented)
}
}
pub fn request_device_permission(kind: DeviceKind) -> Result<PermissionState, CaptureError> {
#[cfg(windows)]
{
mediaway_device::windows::request_permission(kind)
}
#[cfg(target_os = "linux")]
{
mediaway_device::linux::request_permission(kind)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
mediaway_device::apple::request_permission(kind)
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos", target_os = "ios")))]
{
let _ = kind;
Ok(PermissionState::NotSupported)
}
}
#[cfg(test)]
#[path = "platform_tests.rs"]
mod tests;