use std::{
fmt::{Debug, Formatter},
sync::{Arc, Weak},
};
use lazy_static::lazy_static;
use libwebrtc::prelude::*;
use parking_lot::Mutex;
use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))]
use libwebrtc::peer_connection_factory::native::PeerConnectionFactoryExt;
lazy_static! {
static ref LK_RUNTIME: Mutex<LkRuntimeState> = Mutex::new(LkRuntimeState::default());
}
#[derive(Default)]
struct LkRuntimeState {
runtime: Weak<LkRuntime>,
zero_playout_delay: bool,
}
impl LkRuntimeState {
fn enable_zero_playout_delay(
&mut self,
active_runtime_zero_playout_delay: Option<bool>,
) -> Result<(), WebRtcRuntimeInitializedError> {
if active_runtime_zero_playout_delay == Some(false) {
return Err(WebRtcRuntimeInitializedError);
}
self.zero_playout_delay = true;
Ok(())
}
}
#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
#[error("the WebRTC runtime is already initialized without zero playout delay")]
pub struct WebRtcRuntimeInitializedError;
pub struct LkRuntime {
pc_factory: PeerConnectionFactory,
zero_playout_delay: bool,
}
impl Debug for LkRuntime {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.debug_struct("LkRuntime").finish()
}
}
impl LkRuntime {
pub(crate) fn enable_zero_playout_delay() -> Result<(), WebRtcRuntimeInitializedError> {
let mut state = LK_RUNTIME.lock();
let active_runtime_zero_playout_delay =
state.runtime.upgrade().map(|runtime| runtime.zero_playout_delay);
state.enable_zero_playout_delay(active_runtime_zero_playout_delay)
}
pub fn instance() -> Arc<LkRuntime> {
let mut state = LK_RUNTIME.lock();
if let Some(lk_runtime) = state.runtime.upgrade() {
lk_runtime
} else {
log::debug!("LkRuntime::new()");
let zero_playout_delay = state.zero_playout_delay;
#[cfg(not(target_arch = "wasm32"))]
let pc_factory = if zero_playout_delay {
PeerConnectionFactory::with_zero_playout_delay()
} else {
PeerConnectionFactory::default()
};
#[cfg(target_arch = "wasm32")]
let pc_factory = PeerConnectionFactory::default();
let new_runtime = Arc::new(Self { pc_factory, zero_playout_delay });
state.runtime = Arc::downgrade(&new_runtime);
new_runtime
}
}
pub fn pc_factory(&self) -> &PeerConnectionFactory {
&self.pc_factory
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn playout_devices(&self) -> i16 {
self.pc_factory.playout_devices()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn recording_devices(&self) -> i16 {
self.pc_factory.recording_devices()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn playout_device_name(&self, index: u16) -> String {
self.pc_factory.playout_device_name(index)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn recording_device_name(&self, index: u16) -> String {
self.pc_factory.recording_device_name(index)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn playout_device_guid(&self, index: u16) -> String {
self.pc_factory.playout_device_guid(index)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn recording_device_guid(&self, index: u16) -> String {
self.pc_factory.recording_device_guid(index)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn set_playout_device(&self, index: u16) -> bool {
self.pc_factory.set_playout_device(index)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn set_recording_device(&self, index: u16) -> bool {
self.pc_factory.set_recording_device(index)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn set_playout_device_by_guid(&self, guid: &str) -> bool {
self.pc_factory.set_playout_device_by_guid(guid)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn set_recording_device_by_guid(&self, guid: &str) -> bool {
self.pc_factory.set_recording_device_by_guid(guid)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn stop_recording(&self) -> bool {
self.pc_factory.stop_recording()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn init_recording(&self) -> bool {
self.pc_factory.init_recording()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn start_recording(&self) -> bool {
self.pc_factory.start_recording()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn recording_is_initialized(&self) -> bool {
self.pc_factory.recording_is_initialized()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn stop_playout(&self) -> bool {
self.pc_factory.stop_playout()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn init_playout(&self) -> bool {
self.pc_factory.init_playout()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn start_playout(&self) -> bool {
self.pc_factory.start_playout()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn playout_is_initialized(&self) -> bool {
self.pc_factory.playout_is_initialized()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn builtin_aec_is_available(&self) -> bool {
self.pc_factory.builtin_aec_is_available()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn builtin_agc_is_available(&self) -> bool {
self.pc_factory.builtin_agc_is_available()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn builtin_ns_is_available(&self) -> bool {
self.pc_factory.builtin_ns_is_available()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn enable_builtin_aec(&self, enable: bool) -> bool {
self.pc_factory.enable_builtin_aec(enable)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn enable_builtin_agc(&self, enable: bool) -> bool {
self.pc_factory.enable_builtin_agc(enable)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn enable_builtin_ns(&self, enable: bool) -> bool {
self.pc_factory.enable_builtin_ns(enable)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn set_adm_recording_enabled(&self, enabled: bool) {
self.pc_factory.set_adm_recording_enabled(enabled)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn adm_recording_enabled(&self) -> bool {
self.pc_factory.adm_recording_enabled()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn set_adm_playout_enabled(&self, enabled: bool) {
self.pc_factory.set_adm_playout_enabled(enabled)
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn adm_playout_enabled(&self) -> bool {
self.pc_factory.adm_playout_enabled()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn acquire_platform_adm(&self) -> bool {
self.pc_factory.acquire_platform_adm()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn release_platform_adm(&self) {
self.pc_factory.release_platform_adm()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn platform_adm_ref_count(&self) -> i32 {
self.pc_factory.platform_adm_ref_count()
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn is_platform_adm_active(&self) -> bool {
self.pc_factory.is_platform_adm_active()
}
}
#[cfg(test)]
mod tests {
use super::{LkRuntimeState, WebRtcRuntimeInitializedError};
#[test]
fn zero_playout_delay_can_be_enabled_early_and_repeated() {
let mut state = LkRuntimeState::default();
assert_eq!(state.enable_zero_playout_delay(None), Ok(()));
assert!(state.zero_playout_delay);
assert_eq!(state.enable_zero_playout_delay(Some(true)), Ok(()));
}
#[test]
fn zero_playout_delay_rejects_late_enable_on_default_runtime() {
let mut state = LkRuntimeState::default();
assert_eq!(
state.enable_zero_playout_delay(Some(false)),
Err(WebRtcRuntimeInitializedError)
);
assert!(!state.zero_playout_delay);
}
}
impl Drop for LkRuntime {
fn drop(&mut self) {
log::debug!("LkRuntime::drop()");
}
}