pub(crate) mod driver;
pub mod transport;
use crate::peer_connection::transport::{SctpTransport, SctpTransportImpl};
use log::error;
use std::collections::{HashMap, HashSet};
use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::time::{Duration, Instant};
use crate::data_channel::{DataChannel, DataChannelEvent, DataChannelImpl};
use crate::media_stream::{track_local::TrackLocal, track_remote::TrackRemote};
use crate::rtp_transceiver::{RtpReceiver, RtpSender, RtpTransceiver, RtpTransceiverImpl};
use crate::runtime::{JoinHandle, Runtime, default_runtime};
use crate::runtime::{Mutex, Sender, channel};
use std::sync::atomic::{AtomicBool, Ordering};
use driver::{
APPLICATION_TO_DRIVER_EVENT_CHANNEL_CAPACITY, DRIVER_TO_DATA_CHANNEL_EVENT_CHANNEL_CAPACITY,
PeerConnectionDriver,
};
use rtc::data_channel::{RTCDataChannelId, RTCDataChannelInit};
use rtc::ice::mdns::MulticastDnsMode;
use rtc::peer_connection::RTCPeerConnectionBuilder;
use rtc::peer_connection::configuration::{RTCAnswerOptions, RTCOfferOptions};
use rtc::rtp_transceiver::rtp_sender::RtpCodecKind;
use rtc::rtp_transceiver::{RTCRtpTransceiverId, RTCRtpTransceiverInit};
use rtc::sansio::Protocol;
use rtc::shared::error::{Error, Result};
pub use rtc::statistics::StatsSelector;
pub use rtc::statistics::report::{RTCStatsReport, RTCStatsReportEntry};
use crate::media_stream::track_local::TrackLocalEvent;
use crate::media_stream::track_local::static_rtp::TrackLocalStaticRTP;
use crate::media_stream::track_remote::TrackRemoteEvent;
use crate::peer_connection::driver::PeerConnectionDriverEvent;
use crate::rtp_transceiver::rtp_sender::RtpSenderImpl;
pub use rtc::interceptor::Registry;
pub use rtc::crypto;
pub use rtc::dtls::cipher_suite::CipherSuiteId;
pub use rtc::dtls::extension::extension_use_srtp::SrtpProtectionProfile;
use rtc::media_stream::MediaStreamTrackId;
pub use rtc::peer_connection::{
RTCPeerConnection,
certificate::RTCCertificate,
configuration::{
RTCBundlePolicy, RTCConfiguration, RTCConfigurationBuilder, RTCIceServer,
RTCIceTransportPolicy, RTCRtcpMuxPolicy,
interceptor_registry::*,
media_engine::MediaEngine,
setting_engine::{SettingEngine, SettingEngineBuilder},
},
event::{
RTCDataChannelEvent, RTCPeerConnectionEvent, RTCPeerConnectionIceErrorEvent,
RTCPeerConnectionIceEvent, RTCTrackEvent,
},
sdp::{RTCSdpType, RTCSessionDescription},
state::{
RTCIceConnectionState, RTCIceGatheringState, RTCPeerConnectionState, RTCSignalingState,
},
transport::{
RTCIceCandidate, RTCIceCandidateInit, RTCIceCandidateType, RTCIceParameters, RTCIceProtocol,
},
};
#[async_trait::async_trait]
pub trait PeerConnectionEventHandler: Send + Sync + 'static {
async fn on_negotiation_needed(&self) {}
async fn on_ice_candidate(&self, _event: RTCPeerConnectionIceEvent) {}
async fn on_ice_candidate_error(&self, _event: RTCPeerConnectionIceErrorEvent) {}
async fn on_signaling_state_change(&self, _state: RTCSignalingState) {}
async fn on_ice_connection_state_change(&self, _state: RTCIceConnectionState) {}
async fn on_ice_gathering_state_change(&self, _state: RTCIceGatheringState) {}
async fn on_connection_state_change(&self, _state: RTCPeerConnectionState) {}
async fn on_data_channel(&self, _data_channel: Arc<dyn DataChannel>) {}
async fn on_track(&self, _track: Arc<dyn TrackRemote>) {}
}
pub struct PeerConnectionBuilder<A: ToSocketAddrs> {
builder: RTCPeerConnectionBuilder,
runtime: Option<Arc<dyn Runtime>>,
handler: Option<Arc<dyn PeerConnectionEventHandler>>,
udp_addrs: Vec<A>,
tcp_addrs: Vec<A>,
dedicated_reactor_pool_size: usize,
data_channel_send_buffer_limit: usize,
setting_engine: SettingEngine,
}
impl<A: ToSocketAddrs> Default for PeerConnectionBuilder<A> {
fn default() -> Self {
Self {
builder: RTCPeerConnectionBuilder::new(),
runtime: None,
handler: None,
udp_addrs: vec![],
tcp_addrs: vec![],
dedicated_reactor_pool_size: 0,
setting_engine: SettingEngine::default(),
data_channel_send_buffer_limit: usize::MAX,
}
}
}
impl<A: ToSocketAddrs> PeerConnectionBuilder<A> {
pub fn new() -> Self {
Self::default()
}
}
impl<A: ToSocketAddrs> PeerConnectionBuilder<A> {
pub fn with_configuration(mut self, configuration: RTCConfiguration) -> Self {
self.builder = self.builder.with_configuration(configuration);
self
}
pub fn with_media_engine(mut self, media_engine: MediaEngine) -> Self {
self.builder = self.builder.with_media_engine(media_engine);
self
}
pub fn with_setting_engine(mut self, setting_engine: SettingEngine) -> Self {
self.setting_engine = setting_engine;
self
}
pub fn with_interceptor_registry(mut self, interceptor_registry: Registry) -> Self {
self.builder = self.builder.with_interceptor_registry(interceptor_registry);
self
}
pub fn with_runtime(mut self, runtime: Arc<dyn Runtime>) -> Self {
self.runtime = Some(runtime);
self
}
pub fn with_handler(mut self, handler: Arc<dyn PeerConnectionEventHandler>) -> Self {
self.handler = Some(handler);
self
}
pub fn with_udp_addrs(mut self, udp_addrs: Vec<A>) -> Self {
self.udp_addrs = udp_addrs;
self
}
pub fn with_tcp_addrs(mut self, tcp_addrs: Vec<A>) -> Self {
self.tcp_addrs = tcp_addrs;
self
}
pub fn with_dedicated_reactor_pool_size(mut self, dedicated_reactor_pool_size: usize) -> Self {
self.dedicated_reactor_pool_size = dedicated_reactor_pool_size;
self
}
pub fn with_data_channel_send_buffer_limit(mut self, bytes: usize) -> Self {
self.data_channel_send_buffer_limit = bytes;
self
}
pub async fn build(mut self) -> Result<impl PeerConnection>
where
A: Send + 'static,
{
let runtime = if let Some(runtime) = self.runtime {
runtime
} else {
default_runtime().ok_or_else(|| std::io::Error::other("no async runtime found"))?
};
let crypto_provider = match self.setting_engine.crypto_provider() {
Some(provider) => provider.clone(),
None => crypto::default_provider().map_err(|error| {
Error::Crypto(format!(
"failed to resolve a default crypto provider: {error}"
))
})?,
};
self.setting_engine
.set_crypto_provider(crypto_provider.clone());
let mdns_mode = self.setting_engine.multicast_dns().mode;
let turn_allocation_refresh_interval_cap =
self.setting_engine.turn_allocation_refresh_interval_cap();
let discard_local_candidates_during_ice_restart = self
.setting_engine
.discard_local_candidates_during_ice_restart();
let core = self
.builder
.with_setting_engine(self.setting_engine)
.build(runtime.now())?;
let data_channel_send_buffer_limit = if self.data_channel_send_buffer_limit == 0 {
usize::MAX
} else {
self.data_channel_send_buffer_limit
};
PeerConnectionImpl::new(
core,
runtime,
self.handler
.ok_or_else(|| std::io::Error::other("no event handler found"))?,
mdns_mode,
discard_local_candidates_during_ice_restart,
self.udp_addrs,
self.tcp_addrs,
self.dedicated_reactor_pool_size,
data_channel_send_buffer_limit,
turn_allocation_refresh_interval_cap,
crypto_provider,
)
.await
}
}
#[async_trait::async_trait]
pub trait PeerConnection: crate::sealed::Sealed + Send + Sync + 'static {
async fn close(&self) -> Result<()>;
async fn create_offer(&self, options: Option<RTCOfferOptions>)
-> Result<RTCSessionDescription>;
async fn create_answer(
&self,
options: Option<RTCAnswerOptions>,
) -> Result<RTCSessionDescription>;
async fn set_local_description(&self, desc: RTCSessionDescription) -> Result<()>;
async fn local_description(&self) -> Option<RTCSessionDescription>;
async fn current_local_description(&self) -> Option<RTCSessionDescription>;
async fn pending_local_description(&self) -> Option<RTCSessionDescription>;
async fn can_trickle_ice_candidates(&self) -> Option<bool>;
async fn set_remote_description(&self, desc: RTCSessionDescription) -> Result<()>;
async fn remote_description(&self) -> Option<RTCSessionDescription>;
async fn current_remote_description(&self) -> Option<RTCSessionDescription>;
async fn pending_remote_description(&self) -> Option<RTCSessionDescription>;
async fn add_ice_candidate(&self, candidate: RTCIceCandidateInit) -> Result<()>;
async fn restart_ice(&self) -> Result<()>;
async fn get_configuration(&self) -> RTCConfiguration;
async fn set_configuration(&self, configuration: RTCConfiguration) -> Result<()>;
async fn create_data_channel(
&self,
label: &str,
options: Option<RTCDataChannelInit>,
) -> Result<Arc<dyn DataChannel>>;
async fn get_senders(&self) -> Vec<Arc<dyn RtpSender>>;
async fn get_receivers(&self) -> Vec<Arc<dyn RtpReceiver>>;
async fn get_transceivers(&self) -> Vec<Arc<dyn RtpTransceiver>>;
async fn add_track(&self, track: Arc<dyn TrackLocal>) -> Result<Arc<dyn RtpSender>>;
async fn remove_track(&self, sender: &Arc<dyn RtpSender>) -> Result<()>;
async fn add_transceiver_from_track(
&self,
track: Arc<dyn TrackLocal>,
init: Option<RTCRtpTransceiverInit>,
) -> Result<Arc<dyn RtpTransceiver>>;
async fn add_transceiver_from_kind(
&self,
kind: RtpCodecKind,
init: Option<RTCRtpTransceiverInit>,
) -> Result<Arc<dyn RtpTransceiver>>;
async fn get_stats(&self, now: Instant, selector: StatsSelector) -> RTCStatsReport;
async fn sctp(&self) -> Option<Arc<dyn SctpTransport>>;
}
pub(crate) struct PeerConnectionImpl {
inner: Arc<PeerConnectionRef>,
driver_handle: Mutex<Option<Box<dyn JoinHandle>>>,
dedicated_reactor: bool,
}
pub(crate) struct PeerConnectionRef {
pub(crate) core: Mutex<RTCPeerConnection>,
pub(crate) runtime: Arc<dyn Runtime>,
pub(crate) handler: Arc<dyn PeerConnectionEventHandler>,
pub(crate) rtp_transceivers: Mutex<HashMap<RTCRtpTransceiverId, Arc<RtpTransceiverImpl>>>,
pub(crate) driver_event_tx: Sender<PeerConnectionDriverEvent>,
pub(crate) write_pending: AtomicBool,
pub(crate) write_backpressure: std::sync::atomic::AtomicUsize,
pub(crate) closing: AtomicBool,
pub(crate) data_channel_send_buffer_limit: usize,
pub(crate) data_channel_backpressure: crate::runtime::Notify,
pub(crate) data_channel_events_tx: Mutex<HashMap<RTCDataChannelId, Sender<DataChannelEvent>>>,
#[allow(clippy::type_complexity)]
pub(crate) track_remote_events_tx:
Mutex<HashMap<MediaStreamTrackId, (Sender<TrackRemoteEvent>, Arc<dyn TrackRemote>)>>,
pub(crate) track_local_events_tx: Mutex<HashMap<MediaStreamTrackId, Sender<TrackLocalEvent>>>,
pub(crate) data_channel_delivery_blocked: AtomicBool,
pub(crate) data_channel_consumed: crate::runtime::Notify,
pub(crate) discard_local_candidates_during_ice_restart: bool,
pub(crate) ice_restart_rebind_pending: AtomicBool,
}
fn ice_credentials(desc: &RTCSessionDescription) -> Option<(String, String)> {
let parsed = desc.unmarshal().ok()?;
let mut ufrag = parsed.attribute("ice-ufrag").map(String::as_str);
let mut pwd = parsed.attribute("ice-pwd").map(String::as_str);
let (mut backup_ufrag, mut backup_pwd) = (None, None);
for media in &parsed.media_descriptions {
let media_ufrag = media.attribute("ice-ufrag").and_then(|value| value);
let media_pwd = media.attribute("ice-pwd").and_then(|value| value);
if media.attribute("inactive").is_some() {
backup_ufrag = backup_ufrag.or(media_ufrag);
backup_pwd = backup_pwd.or(media_pwd);
continue;
}
ufrag = ufrag.or(media_ufrag);
pwd = pwd.or(media_pwd);
}
Some((
ufrag.or(backup_ufrag)?.to_owned(),
pwd.or(backup_pwd)?.to_owned(),
))
}
fn will_restart_ice(core: &RTCPeerConnection, desc: &RTCSessionDescription) -> bool {
if desc.sdp_type == RTCSdpType::Answer {
return false;
}
let Some(current) = core.current_remote_description() else {
return false;
};
let (Some((current_ufrag, current_pwd)), Some((ufrag, pwd))) =
(ice_credentials(current), ice_credentials(desc))
else {
return false;
};
ufrag != current_ufrag || pwd != current_pwd
}
const WRITE_YIELD_INTERVAL: usize = 128;
impl PeerConnectionRef {
#[inline]
pub(crate) fn mark_ice_restart_rebind_pending(&self) {
if self.discard_local_candidates_during_ice_restart {
self.ice_restart_rebind_pending
.store(true, Ordering::Release);
}
}
#[inline]
pub(crate) async fn wake_writes(&self) {
if !self.write_pending.swap(true, Ordering::AcqRel) {
let _ = self
.driver_event_tx
.try_send(PeerConnectionDriverEvent::WriteNotify);
} else if self.write_backpressure.fetch_add(1, Ordering::Relaxed) % WRITE_YIELD_INTERVAL
== WRITE_YIELD_INTERVAL - 1
{
self.runtime.yield_now().await;
}
}
}
impl PeerConnectionImpl {
#[allow(clippy::too_many_arguments)] async fn new<A: ToSocketAddrs + Send + 'static>(
core: RTCPeerConnection,
runtime: Arc<dyn Runtime>,
handler: Arc<dyn PeerConnectionEventHandler>,
mdns_mode: MulticastDnsMode,
discard_local_candidates_during_ice_restart: bool,
udp_addrs: Vec<A>,
tcp_addrs: Vec<A>,
dedicated_reactor_pool_size: usize,
data_channel_send_buffer_limit: usize,
turn_allocation_refresh_interval_cap: Option<Duration>,
crypto_provider: Arc<dyn crypto::RTCCryptoProvider>,
) -> Result<Self> {
let configuration = core.get_configuration();
let ice_servers = configuration.ice_servers().to_vec();
let ice_gather_policy = configuration.ice_transport_policy();
let (driver_event_tx, driver_event_rx) =
channel(APPLICATION_TO_DRIVER_EVENT_CHANNEL_CAPACITY);
let peer_connection = Self {
inner: Arc::new(PeerConnectionRef {
core: Mutex::new(core),
runtime: runtime.clone(),
data_channel_events_tx: Mutex::new(HashMap::new()),
track_remote_events_tx: Mutex::new(HashMap::new()),
track_local_events_tx: Mutex::new(HashMap::new()),
rtp_transceivers: Mutex::new(HashMap::new()),
handler,
driver_event_tx,
write_pending: AtomicBool::new(false),
write_backpressure: std::sync::atomic::AtomicUsize::new(0),
closing: AtomicBool::new(false),
data_channel_send_buffer_limit,
data_channel_backpressure: crate::runtime::Notify::new(),
data_channel_delivery_blocked: AtomicBool::new(false),
data_channel_consumed: crate::runtime::Notify::new(),
discard_local_candidates_during_ice_restart,
ice_restart_rebind_pending: AtomicBool::new(false),
}),
driver_handle: Mutex::new(None),
dedicated_reactor: dedicated_reactor_pool_size > 0,
};
let (init_tx, mut init_rx) = channel::<Result<()>>(1);
let inner = peer_connection.inner.clone();
let run_driver = async move {
let mut driver = PeerConnectionDriver::new(
inner,
udp_addrs,
tcp_addrs,
mdns_mode,
ice_servers,
ice_gather_policy,
crypto_provider,
discard_local_candidates_during_ice_restart,
turn_allocation_refresh_interval_cap,
);
if let Err(e) = driver.event_loop(driver_event_rx, init_tx).await {
error!("I/O error: {}", e);
}
driver.signal_stopped();
};
let driver_handle = if dedicated_reactor_pool_size > 0 {
runtime.spawn_reactor(dedicated_reactor_pool_size, Box::pin(run_driver))
} else {
runtime.spawn(Box::pin(run_driver))
};
*peer_connection.driver_handle.lock().await = Some(driver_handle);
match init_rx.recv().await {
Some(Ok(())) => Ok(peer_connection),
Some(Err(e)) => Err(e),
None => Err(Error::Other(
"peer connection driver stopped before initialization".to_owned(),
)),
}
}
}
impl Drop for PeerConnectionImpl {
fn drop(&mut self) {
if self.dedicated_reactor {
self.inner.closing.store(true, Ordering::Release);
self.inner.data_channel_backpressure.notify_waiters();
let _ = self
.inner
.driver_event_tx
.try_send(PeerConnectionDriverEvent::Close);
}
}
}
impl crate::sealed::Sealed for PeerConnectionImpl {}
#[async_trait::async_trait]
impl PeerConnection for PeerConnectionImpl {
async fn close(&self) -> Result<()> {
{
let mut core = self.inner.core.lock().await;
core.close()?;
}
self.inner.closing.store(true, Ordering::Release);
self.inner.data_channel_backpressure.notify_waiters();
let _ = self
.inner
.driver_event_tx
.send(PeerConnectionDriverEvent::Close)
.await;
let driver_handle = self.driver_handle.lock().await.take();
if let Some(driver_handle) = driver_handle {
if self.dedicated_reactor {
let step = std::time::Duration::from_millis(1);
let max = std::time::Duration::from_secs(2);
let mut waited = std::time::Duration::ZERO;
while !driver_handle.is_finished() && waited < max {
self.inner.runtime.sleep(step).await;
waited += step;
}
driver_handle.abort();
} else {
driver_handle.abort();
}
}
Ok(())
}
async fn create_offer(
&self,
options: Option<RTCOfferOptions>,
) -> Result<RTCSessionDescription> {
if options.as_ref().is_some_and(|options| options.ice_restart) {
self.inner.mark_ice_restart_rebind_pending();
}
let mut core = self.inner.core.lock().await;
core.create_offer(options)
}
async fn create_answer(
&self,
options: Option<RTCAnswerOptions>,
) -> Result<RTCSessionDescription> {
let mut core = self.inner.core.lock().await;
core.create_answer(options)
}
async fn set_local_description(&self, desc: RTCSessionDescription) -> Result<()> {
{
let mut core = self.inner.core.lock().await;
core.set_local_description(self.inner.runtime.now(), desc)?;
}
self.inner
.driver_event_tx
.send(PeerConnectionDriverEvent::IceGathering)
.await
.map_err(|e| Error::Other(format!("{:?}", e)))
}
async fn local_description(&self) -> Option<RTCSessionDescription> {
let core = self.inner.core.lock().await;
core.local_description()
}
async fn current_local_description(&self) -> Option<RTCSessionDescription> {
let core = self.inner.core.lock().await;
core.current_local_description()
}
async fn pending_local_description(&self) -> Option<RTCSessionDescription> {
let core = self.inner.core.lock().await;
core.pending_local_description()
}
async fn can_trickle_ice_candidates(&self) -> Option<bool> {
let core = self.inner.core.lock().await;
core.can_trickle_ice_candidates()
}
async fn set_remote_description(&self, desc: RTCSessionDescription) -> Result<()> {
{
let mut core = self.inner.core.lock().await;
if self.inner.discard_local_candidates_during_ice_restart
&& will_restart_ice(&core, &desc)
{
self.inner.mark_ice_restart_rebind_pending();
}
core.set_remote_description(self.inner.runtime.now(), desc)?;
}
self.inner.wake_writes().await;
Ok(())
}
async fn remote_description(&self) -> Option<RTCSessionDescription> {
let core = self.inner.core.lock().await;
core.remote_description().cloned()
}
async fn current_remote_description(&self) -> Option<RTCSessionDescription> {
let core = self.inner.core.lock().await;
core.current_remote_description().cloned()
}
async fn pending_remote_description(&self) -> Option<RTCSessionDescription> {
let core = self.inner.core.lock().await;
core.pending_remote_description().cloned()
}
async fn add_ice_candidate(&self, candidate: RTCIceCandidateInit) -> Result<()> {
{
let mut core = self.inner.core.lock().await;
core.add_remote_candidate(candidate.clone())?;
}
let candidate_str = match candidate.candidate.strip_prefix("candidate:") {
Some(s) => s,
None => candidate.candidate.as_str(),
};
if let Ok(c) = rtc::ice::candidate::unmarshal_candidate(candidate_str)
&& c.network_type().is_tcp()
&& c.tcp_type() == rtc::ice::tcp_type::TcpType::Passive
{
self.inner
.driver_event_tx
.send(PeerConnectionDriverEvent::RemoteIceTcpPassiveCandidate(c))
.await
.map_err(|e| Error::Other(format!("{:?}", e)))
} else {
Ok(())
}
}
async fn restart_ice(&self) -> Result<()> {
{
let mut core = self.inner.core.lock().await;
core.restart_ice();
}
self.inner.mark_ice_restart_rebind_pending();
self.inner
.driver_event_tx
.send(PeerConnectionDriverEvent::IceGathering)
.await
.map_err(|e| Error::Other(format!("{:?}", e)))
}
async fn get_configuration(&self) -> RTCConfiguration {
let core = self.inner.core.lock().await;
core.get_configuration().clone()
}
async fn set_configuration(&self, configuration: RTCConfiguration) -> Result<()> {
let (ice_servers, ice_transport_policy) = {
let mut core = self.inner.core.lock().await;
core.set_configuration(configuration)?;
let configuration = core.get_configuration();
(
configuration.ice_servers().to_vec(),
configuration.ice_transport_policy(),
)
};
self.inner
.driver_event_tx
.send(PeerConnectionDriverEvent::UpdateIceConfiguration {
ice_servers,
ice_transport_policy,
})
.await
.map_err(|_| Error::Other("peer connection driver stopped".to_owned()))
}
async fn create_data_channel(
&self,
label: &str,
options: Option<RTCDataChannelInit>,
) -> Result<Arc<dyn DataChannel>> {
let channel_id = {
let mut core = self.inner.core.lock().await;
let rtc_dc = core.create_data_channel(label, options)?;
rtc_dc.id()
};
let (evt_tx, evt_rx) = channel(DRIVER_TO_DATA_CHANNEL_EVENT_CHANNEL_CAPACITY);
{
let mut data_channels = self.inner.data_channel_events_tx.lock().await;
data_channels.insert(channel_id, evt_tx);
}
self.inner.wake_writes().await;
Ok(Arc::new(DataChannelImpl::new(
channel_id,
self.inner.clone(),
evt_rx,
)))
}
async fn get_senders(&self) -> Vec<Arc<dyn RtpSender>> {
let mut rtp_senders = vec![];
for rtp_transceiver in self.get_transceivers().await {
if let Ok(sender) = rtp_transceiver.sender().await
&& let Some(rtp_sender) = sender
{
rtp_senders.push(rtp_sender);
}
}
rtp_senders
}
async fn get_receivers(&self) -> Vec<Arc<dyn RtpReceiver>> {
let mut rtp_receivers = vec![];
for rtp_transceiver in self.get_transceivers().await {
if let Ok(receiver) = rtp_transceiver.receiver().await
&& let Some(rtp_receiver) = receiver
{
rtp_receivers.push(rtp_receiver);
}
}
rtp_receivers
}
async fn get_transceivers(&self) -> Vec<Arc<dyn RtpTransceiver>> {
let current_transceiver_ids: HashSet<RTCRtpTransceiverId> = {
let core = self.inner.core.lock().await;
core.get_transceivers().collect::<HashSet<_>>()
};
let mut rtp_transceivers = self.inner.rtp_transceivers.lock().await;
rtp_transceivers.retain(|id, _| current_transceiver_ids.contains(id));
for id in current_transceiver_ids {
rtp_transceivers
.entry(id)
.or_insert_with(|| Arc::new(RtpTransceiverImpl::new(id, Arc::clone(&self.inner))));
}
rtp_transceivers
.values()
.cloned()
.map(|t| t as Arc<dyn RtpTransceiver>)
.collect()
}
async fn add_track(&self, track: Arc<dyn TrackLocal>) -> Result<Arc<dyn RtpSender>> {
let id: RTCRtpTransceiverId = {
let mut core = self.inner.core.lock().await;
core.add_track(track.track().await)?.into()
};
let mut rtp_transceivers = self.inner.rtp_transceivers.lock().await;
rtp_transceivers
.entry(id)
.or_insert_with(|| Arc::new(RtpTransceiverImpl::new(id, Arc::clone(&self.inner))));
let rtp_transceiver = rtp_transceivers
.get(&id)
.ok_or(Error::ErrRTPTransceiverNotExisted)?;
let sender: Arc<dyn RtpSender> = Arc::new(RtpSenderImpl::new(
id.into(),
Arc::clone(&self.inner),
track,
));
rtp_transceiver.set_sender(Some(Arc::clone(&sender))).await;
Ok(sender)
}
async fn remove_track(&self, sender: &Arc<dyn RtpSender>) -> Result<()> {
{
let mut core = self.inner.core.lock().await;
core.remove_track(sender.id())?;
}
let rtp_transceivers = self.inner.rtp_transceivers.lock().await;
let rtp_transceiver = rtp_transceivers
.get(&sender.id().into())
.ok_or(Error::ErrRTPTransceiverNotExisted)?;
rtp_transceiver.set_sender(None).await;
Ok(())
}
async fn add_transceiver_from_track(
&self,
track: Arc<dyn TrackLocal>,
init: Option<RTCRtpTransceiverInit>,
) -> Result<Arc<dyn RtpTransceiver>> {
let id: RTCRtpTransceiverId = {
let mut core = self.inner.core.lock().await;
core.add_transceiver_from_track(track.track().await, init)?
};
let mut rtp_transceivers = self.inner.rtp_transceivers.lock().await;
rtp_transceivers
.entry(id)
.or_insert_with(|| Arc::new(RtpTransceiverImpl::new(id, Arc::clone(&self.inner))));
let rtp_transceiver = rtp_transceivers
.get(&id)
.ok_or(Error::ErrRTPTransceiverNotExisted)?;
let sender: Arc<dyn RtpSender> = Arc::new(RtpSenderImpl::new(
id.into(),
Arc::clone(&self.inner),
track,
));
rtp_transceiver.set_sender(Some(sender)).await;
Ok(rtp_transceiver.clone() as Arc<dyn RtpTransceiver>)
}
async fn add_transceiver_from_kind(
&self,
kind: RtpCodecKind,
init: Option<RTCRtpTransceiverInit>,
) -> Result<Arc<dyn RtpTransceiver>> {
let (id, track) = {
let mut core = self.inner.core.lock().await;
let id = core.add_transceiver_from_kind(kind, init)?;
(
id,
core.rtp_sender(id.into())
.map(|sender| sender.track().clone()),
)
};
let mut rtp_transceivers = self.inner.rtp_transceivers.lock().await;
rtp_transceivers
.entry(id)
.or_insert_with(|| Arc::new(RtpTransceiverImpl::new(id, Arc::clone(&self.inner))));
let rtp_transceiver = rtp_transceivers
.get(&id)
.ok_or(Error::ErrRTPTransceiverNotExisted)?;
if let Some(track) = track {
let sender: Arc<dyn RtpSender> = Arc::new(RtpSenderImpl::new(
id.into(),
Arc::clone(&self.inner),
Arc::new(TrackLocalStaticRTP::new(track)),
));
rtp_transceiver.set_sender(Some(sender)).await;
}
Ok(rtp_transceiver.clone() as Arc<dyn RtpTransceiver>)
}
async fn get_stats(&self, now: Instant, selector: StatsSelector) -> RTCStatsReport {
let mut core = self.inner.core.lock().await;
core.get_stats(now, selector)
}
async fn sctp(&self) -> Option<Arc<dyn SctpTransport>> {
let core = self.inner.core.lock().await;
let (id, dtls_id, ice_id) = {
let sctp = core.sctp()?;
let dtls = sctp.transport();
(sctp.id(), dtls.id(), dtls.ice_transport().id())
};
Some(Arc::new(SctpTransportImpl::new(
id,
dtls_id,
ice_id,
Arc::clone(&self.inner),
)))
}
}
#[cfg(all(test, any(feature = "crypto-ring", feature = "crypto-aws-lc-rs")))]
pub(crate) use tests::new_test_peer_connection;
#[cfg(all(test, any(feature = "crypto-ring", feature = "crypto-aws-lc-rs")))]
mod tests {
use super::*;
use crate::runtime::{channel, default_runtime, timeout};
use rtc::peer_connection::RTCPeerConnectionBuilder;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::time::Duration;
#[derive(Clone)]
struct DummyHandler;
#[async_trait::async_trait]
impl PeerConnectionEventHandler for DummyHandler {}
pub(crate) async fn new_test_peer_connection() -> (
Arc<PeerConnectionRef>,
crate::runtime::Receiver<PeerConnectionDriverEvent>,
) {
new_test_peer_connection_with_rebind(false).await
}
pub(crate) async fn new_test_peer_connection_with_rebind(
discard_local_candidates_during_ice_restart: bool,
) -> (
Arc<PeerConnectionRef>,
crate::runtime::Receiver<PeerConnectionDriverEvent>,
) {
let core = RTCPeerConnectionBuilder::new()
.build(Instant::now())
.unwrap();
let runtime = default_runtime().expect("test requires a runtime feature");
let handler: Arc<dyn PeerConnectionEventHandler> = Arc::new(DummyHandler);
let (driver_event_tx, driver_event_rx) = channel::<PeerConnectionDriverEvent>(1);
let inner = Arc::new(PeerConnectionRef {
core: Mutex::new(core),
runtime,
handler,
driver_event_tx,
write_pending: AtomicBool::new(false),
write_backpressure: AtomicUsize::new(0),
closing: AtomicBool::new(false),
data_channel_send_buffer_limit: usize::MAX,
data_channel_backpressure: crate::runtime::Notify::new(),
data_channel_delivery_blocked: AtomicBool::new(false),
data_channel_consumed: crate::runtime::Notify::new(),
data_channel_events_tx: Mutex::new(HashMap::new()),
track_remote_events_tx: Mutex::new(HashMap::new()),
track_local_events_tx: Mutex::new(HashMap::new()),
rtp_transceivers: Mutex::new(HashMap::new()),
discard_local_candidates_during_ice_restart,
ice_restart_rebind_pending: AtomicBool::new(false),
});
(inner, driver_event_rx)
}
#[test]
fn mark_ice_restart_rebind_pending_is_gated_on_the_setting() {
let rt = default_runtime().expect("test requires a runtime feature");
rt.block_on(Box::pin(async move {
for enabled in [false, true] {
let (inner, _rx) = new_test_peer_connection_with_rebind(enabled).await;
assert!(
!inner.ice_restart_rebind_pending.load(Ordering::Acquire),
"nothing is armed before a restart"
);
inner.mark_ice_restart_rebind_pending();
assert_eq!(
enabled,
inner.ice_restart_rebind_pending.load(Ordering::Acquire),
"rebind armed only when the setting asked for it"
);
}
}));
}
#[test]
fn build_reports_a_bind_failure_instead_of_returning_a_dead_connection() {
let rt = default_runtime().expect("test requires a runtime feature");
rt.clone().block_on(Box::pin(async move {
let result = PeerConnectionBuilder::new()
.with_handler(Arc::new(DummyHandler))
.with_runtime(rt)
.with_udp_addrs(vec!["1.2.3.4:5"])
.build()
.await;
assert!(
result.is_err(),
"build() must surface the driver's bind failure"
);
}));
}
#[test]
fn create_data_channel_wakes_driver() {
let rt = default_runtime().expect("test requires a runtime feature");
rt.block_on(Box::pin(async {
let (inner, mut driver_event_rx) = new_test_peer_connection().await;
let pc = PeerConnectionImpl {
inner,
driver_handle: Mutex::new(None),
dedicated_reactor: false,
};
let _dc = pc.create_data_channel("test", None).await.unwrap();
let event = timeout(&*rt, Duration::from_secs(1), driver_event_rx.recv())
.await
.expect("driver should be woken within 1s")
.expect("driver event channel should not be closed");
assert!(matches!(event, PeerConnectionDriverEvent::WriteNotify));
}));
}
}
#[cfg(all(test, feature = "runtime-mock"))]
mod virtual_clock_tests {
use super::*;
use crate::runtime::mock::{MockRuntime, MockUDPNetwork};
use std::sync::Mutex as StdMutex;
use std::time::Duration;
#[derive(Debug, Default)]
struct StateRecorder {
ice: StdMutex<Vec<RTCIceConnectionState>>,
pending_candidates: StdMutex<Vec<RTCIceCandidateInit>>,
conn: StdMutex<Vec<RTCPeerConnectionState>>,
}
impl StateRecorder {
fn ice_states(&self) -> Vec<RTCIceConnectionState> {
self.ice.lock().expect("recorder poisoned").clone()
}
fn saw_ice(&self, state: RTCIceConnectionState) -> bool {
self.ice_states().contains(&state)
}
fn saw_conn(&self, state: RTCPeerConnectionState) -> bool {
self.conn
.lock()
.expect("recorder poisoned")
.contains(&state)
}
fn conn_states(&self) -> Vec<RTCPeerConnectionState> {
self.conn.lock().expect("recorder poisoned").clone()
}
fn take_candidates(&self) -> Vec<RTCIceCandidateInit> {
std::mem::take(&mut *self.pending_candidates.lock().expect("recorder poisoned"))
}
}
#[async_trait::async_trait]
impl PeerConnectionEventHandler for StateRecorder {
async fn on_ice_connection_state_change(&self, state: RTCIceConnectionState) {
self.ice.lock().expect("recorder poisoned").push(state);
}
async fn on_connection_state_change(&self, state: RTCPeerConnectionState) {
self.conn.lock().expect("recorder poisoned").push(state);
}
async fn on_ice_candidate(&self, event: RTCPeerConnectionIceEvent) {
if let Ok(init) = event.candidate.to_json() {
self.pending_candidates
.lock()
.expect("recorder poisoned")
.push(init);
}
}
}
fn settle() {
std::thread::yield_now();
std::thread::sleep(Duration::from_millis(2));
}
fn advance_both(a: &MockRuntime, b: &MockRuntime, delta: Duration) {
a.clock().advance(delta);
b.clock().advance(delta);
settle();
}
struct Peer {
pc: Box<dyn PeerConnection>,
rt: Arc<MockRuntime>,
rec: Arc<StateRecorder>,
}
const CLOCK_OFFSET: Duration = Duration::from_secs(3600);
async fn build_peer(network: &Arc<MockUDPNetwork>) -> Peer {
let rt = Arc::new(MockRuntime::with_network(Arc::clone(network)));
rt.clock().advance(CLOCK_OFFSET);
let rec = Arc::new(StateRecorder::default());
let setting_engine = SettingEngineBuilder::new()
.with_multicast_dns_mode(rtc::ice::mdns::MulticastDnsMode::Disabled)
.build();
let pc = PeerConnectionBuilder::new()
.with_setting_engine(setting_engine)
.with_runtime(Arc::clone(&rt) as Arc<dyn Runtime>)
.with_handler(Arc::clone(&rec) as Arc<dyn PeerConnectionEventHandler>)
.with_udp_addrs(vec!["127.0.0.1:0"])
.build()
.await
.expect("a peer connection builds on the mock runtime");
Peer {
pc: Box::new(pc),
rt,
rec,
}
}
async fn connect_pair(network: &Arc<MockUDPNetwork>) -> (Peer, Peer) {
let offerer = build_peer(network).await;
let answerer = build_peer(network).await;
offerer
.pc
.create_data_channel("probe", None)
.await
.expect("create data channel");
let offer = offerer.pc.create_offer(None).await.expect("create offer");
offerer
.pc
.set_local_description(offer.clone())
.await
.expect("set local description");
answerer
.pc
.set_remote_description(offer)
.await
.expect("set remote description");
let answer = answerer
.pc
.create_answer(None)
.await
.expect("create answer");
answerer
.pc
.set_local_description(answer.clone())
.await
.expect("set local description");
offerer
.pc
.set_remote_description(answer)
.await
.expect("set remote description");
for _ in 0..200 {
for c in offerer.rec.take_candidates() {
answerer.pc.add_ice_candidate(c).await.ok();
}
for c in answerer.rec.take_candidates() {
offerer.pc.add_ice_candidate(c).await.ok();
}
if offerer.rec.saw_ice(RTCIceConnectionState::Connected)
&& answerer.rec.saw_ice(RTCIceConnectionState::Connected)
{
return (offerer, answerer);
}
advance_both(&offerer.rt, &answerer.rt, Duration::from_millis(50));
}
panic!(
"ICE did not connect under the virtual clock; offerer saw {:?}, answerer saw {:?}",
offerer.rec.ice_states(),
answerer.rec.ice_states()
);
}
#[test]
fn dtls_and_sctp_complete_under_a_virtual_clock() {
let network = Arc::new(MockUDPNetwork::new());
let driver = MockRuntime::new();
let wall_clock_start = std::time::Instant::now();
driver.block_on(Box::pin(async {
let (offerer, answerer) = connect_pair(&network).await;
let mut opened = false;
for _ in 0..400 {
if offerer.rec.saw_conn(RTCPeerConnectionState::Connected)
&& answerer.rec.saw_conn(RTCPeerConnectionState::Connected)
{
opened = true;
break;
}
advance_both(&offerer.rt, &answerer.rt, Duration::from_millis(25));
}
assert!(
opened,
"DTLS + SCTP must complete on the virtual clock; offerer {:?}, answerer {:?}",
offerer.rec.conn_states(),
answerer.rec.conn_states()
);
}));
assert!(
wall_clock_start.elapsed() < Duration::from_secs(5),
"a virtual-clock test must not spend real time: took {:?}",
wall_clock_start.elapsed()
);
}
#[test]
fn ice_consent_expires_when_only_the_virtual_clock_advances() {
let network = Arc::new(MockUDPNetwork::new());
let driver = MockRuntime::new();
let wall_clock_start = std::time::Instant::now();
driver.block_on(Box::pin(async {
let (offerer, answerer) = connect_pair(&network).await;
answerer.pc.close().await.expect("close answerer");
settle();
settle();
assert!(
!offerer.rec.saw_ice(RTCIceConnectionState::Disconnected),
"closing the peer must not by itself disconnect the offerer"
);
for _ in 0..120 {
if offerer.rec.saw_ice(RTCIceConnectionState::Disconnected)
|| offerer.rec.saw_ice(RTCIceConnectionState::Failed)
{
break;
}
offerer.rt.clock().advance(Duration::from_secs(1));
settle();
}
let states = offerer.rec.ice_states();
assert!(
states.contains(&RTCIceConnectionState::Disconnected)
|| states.contains(&RTCIceConnectionState::Failed),
"consent should have expired once the virtual clock passed the window; saw {states:?}"
);
}));
assert!(
wall_clock_start.elapsed() < Duration::from_secs(5),
"a virtual-clock test must not spend real time: took {:?}",
wall_clock_start.elapsed()
);
}
#[test]
fn advancing_the_mock_clock_does_not_advance_the_wall_clock() {
let rt = MockRuntime::new();
let clock = rt.clock();
let virtual_before = rt.now();
let wall_before = std::time::Instant::now();
clock.advance(Duration::from_secs(30));
assert_eq!(
rt.now().duration_since(virtual_before),
Duration::from_secs(30),
"the runtime's clock must report exactly what was advanced"
);
assert!(
wall_before.elapsed() < Duration::from_millis(500),
"advancing virtual time must not sleep: {:?} of real time passed",
wall_before.elapsed()
);
}
#[test]
fn clocks_are_independent_across_runtimes() {
let a = MockRuntime::new();
let b = MockRuntime::new();
let b_before = b.now();
a.clock().advance(Duration::from_secs(60));
assert_eq!(
b.now(),
b_before,
"advancing one clock must not move another"
);
assert!(a.now() > b.now());
}
#[test]
fn ice_connects_under_a_virtual_clock() {
let network = Arc::new(MockUDPNetwork::new());
let driver = MockRuntime::new();
driver.block_on(Box::pin(async {
let (offerer, answerer) = connect_pair(&network).await;
assert!(offerer.rec.saw_ice(RTCIceConnectionState::Connected));
assert!(answerer.rec.saw_ice(RTCIceConnectionState::Connected));
}));
}
}