mod builder;
mod config;
mod encoded;
mod media;
mod observer;
mod peer_connection;
pub mod platform;
use std::collections::VecDeque;
use std::ffi::CString;
use std::os::raw::c_int;
use std::sync::{Arc, Mutex};
pub use builder::{EncodedVideoBuilder, MixedVideoTrack};
pub use config::{ContinualGatheringPolicy, IceServer, IceTransportsType, RtcConfiguration};
pub use encoded::{
CustomVideoEncoder, EncodedFrame, EncodedVideoFrame, EncodedVideoTrack, FrameAction,
FrameDirection, FrameTransform, RawVideoFrame, VideoCodec,
};
pub use media::{AudioFrame, MediaKind, Track, VideoFrame};
pub use observer::PeerConnectionObserver;
pub use peer_connection::{
DataChannel, DataChannelState, IceCandidate, IceCandidatePairState, IceCandidatePairStats,
IceGatheringState, InboundRtpStats, OutboundRtpStats, PeerConnection, PeerConnectionState,
SdpType, SessionDescription, StatsReport, Transceiver, TransceiverDirection,
};
pub fn native_abi_version() -> u32 {
unsafe { reactor_webrtc_sys::reactor_webrtc_abi_version() }
}
#[derive(Debug, Clone)]
pub enum Error {
Webrtc(String),
NotFound(String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Webrtc(m) => write!(f, "webrtc error: {m}"),
Error::NotFound(m) => write!(f, "not found: {m}"),
}
}
}
impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AdmMode {
#[default]
Synthetic,
Platform,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ApmConfig {
pub echo_canceller: bool,
pub noise_suppression: bool,
pub agc: bool,
pub high_pass_filter: bool,
}
impl ApmConfig {
fn to_flags(self) -> c_int {
let mut f: c_int = 0;
if self.echo_canceller {
f |= 0x01;
}
if self.noise_suppression {
f |= 0x02;
}
if self.agc {
f |= 0x04;
}
if self.high_pass_filter {
f |= 0x08;
}
f
}
}
pub struct PeerConnectionFactory {
raw: *mut reactor_webrtc_sys::PeerConnectionFactory,
}
unsafe impl Send for PeerConnectionFactory {}
unsafe impl Sync for PeerConnectionFactory {}
impl PeerConnectionFactory {
pub fn with_adm(mode: AdmMode) -> Result<Self> {
Self::with_adm_apm(mode, ApmConfig::default())
}
pub fn with_adm_apm(mode: AdmMode, apm: ApmConfig) -> Result<Self> {
let raw = unsafe {
reactor_webrtc_sys::reactor_webrtc_factory_create_with_adm_apm(
matches!(mode, AdmMode::Platform) as c_int,
apm.to_flags(),
)
};
if raw.is_null() {
return Err(Error::Webrtc("factory creation returned null".into()));
}
Ok(Self { raw })
}
pub fn new() -> Result<Self> {
Self::with_adm(AdmMode::Synthetic)
}
pub fn with_platform_adm() -> Result<Self> {
Self::with_adm_apm(
AdmMode::Platform,
ApmConfig {
echo_canceller: true,
noise_suppression: true,
agc: true,
high_pass_filter: true,
},
)
}
pub fn with_custom_video_encoder(encoder: crate::CustomVideoEncoder) -> Result<Self> {
let encode_fn = encoder.encode_fn;
let userdata = encoder.userdata;
let free_ud = encoder.free_ud;
let use_builtin = encoder.use_builtin;
let raw = unsafe {
reactor_webrtc_sys::reactor_webrtc_factory_create_with_custom_video_encoder(
0, encode_fn,
userdata,
free_ud,
use_builtin,
0, )
};
if raw.is_null() {
if let Some(f) = free_ud {
f(userdata);
}
return Err(Error::Webrtc(
"factory with custom encoder returned null".into(),
));
}
Ok(Self { raw })
}
pub fn encoded_video_builder() -> EncodedVideoBuilder {
EncodedVideoBuilder::new()
}
pub fn with_encoded_video_track(
track_id: &str,
width: u32,
height: u32,
) -> Result<(Self, crate::EncodedVideoTrack)> {
let queue: Arc<Mutex<VecDeque<EncodedVideoFrame>>> = Arc::new(Mutex::new(VecDeque::new()));
let encoder = crate::CustomVideoEncoder::from_queue(queue.clone());
let factory = Self::with_custom_video_encoder(encoder)?;
let track = factory.create_video_track(track_id)?;
let encoded = crate::EncodedVideoTrack::new(track, queue, width, height);
Ok((factory, encoded))
}
pub fn create_peer_connection(
&self,
config: &RtcConfiguration,
observer: PeerConnectionObserver,
) -> Result<PeerConnection> {
let state = observer.into_state();
let callbacks = state.callbacks();
let json = CString::new(config.to_json())
.map_err(|_| Error::Webrtc("config contains a NUL byte".into()))?;
let raw = unsafe {
reactor_webrtc_sys::reactor_webrtc_peer_connection_create(
self.raw,
json.as_ptr(),
&callbacks,
)
};
if raw.is_null() {
return Err(Error::Webrtc(
"peer connection creation returned null".into(),
));
}
Ok(PeerConnection::new(raw, state))
}
pub fn create_video_track(&self, id: &str) -> Result<Track> {
let cid = CString::new(id).map_err(|_| Error::Webrtc("id contains a NUL byte".into()))?;
let raw = unsafe {
reactor_webrtc_sys::reactor_webrtc_video_track_create(self.raw, cid.as_ptr())
};
if raw.is_null() {
return Err(Error::Webrtc("video track creation returned null".into()));
}
Ok(Track::from_raw(raw, MediaKind::Video))
}
pub fn create_audio_track(&self, id: &str) -> Result<Track> {
let cid = CString::new(id).map_err(|_| Error::Webrtc("id contains a NUL byte".into()))?;
let raw = unsafe {
reactor_webrtc_sys::reactor_webrtc_audio_track_create(self.raw, cid.as_ptr())
};
if raw.is_null() {
return Err(Error::Webrtc("audio track creation returned null".into()));
}
Ok(Track::from_raw(raw, MediaKind::Audio))
}
pub fn push_audio_frame(&self, pcm: &[i16], sample_rate: u32, channels: u32) {
let channels = channels.max(1);
let samples_per_channel = (pcm.len() / channels as usize) as c_int;
unsafe {
reactor_webrtc_sys::reactor_webrtc_factory_push_audio_frame(
self.raw,
pcm.as_ptr(),
samples_per_channel,
sample_rate as c_int,
channels as c_int,
);
}
}
pub fn set_adm_playout_enabled(&self, enabled: bool) {
unsafe {
reactor_webrtc_sys::reactor_webrtc_factory_set_adm_playout_enabled(
self.raw,
enabled as c_int,
)
}
}
}
impl Drop for PeerConnectionFactory {
fn drop(&mut self) {
unsafe { reactor_webrtc_sys::reactor_webrtc_factory_destroy(self.raw) }
}
}