pub mod certificate;
pub mod configuration;
pub mod event;
pub(crate) mod handler;
mod internal;
pub mod message;
pub mod sdp;
pub mod state;
pub mod transport;
use crate::data_channel::init::RTCDataChannelInit;
use crate::data_channel::parameters::DataChannelParameters;
use crate::data_channel::{RTCDataChannel, RTCDataChannelId, internal::RTCDataChannelInternal};
use crate::media_stream::track::MediaStreamTrack;
use crate::peer_connection::configuration::media_engine::MediaEngine;
use crate::peer_connection::configuration::setting_engine::{SctpMaxMessageSize, SettingEngine};
use crate::peer_connection::configuration::{
RTCConfiguration, RTCIceTransportPolicy,
offer_answer_options::{RTCAnswerOptions, RTCOfferOptions},
};
use crate::peer_connection::event::RTCPeerConnectionEvent;
use crate::peer_connection::handler::PipelineContext;
use crate::peer_connection::handler::dtls::DtlsHandlerContext;
use crate::peer_connection::handler::ice::IceHandlerContext;
use crate::peer_connection::handler::sctp::SctpHandlerContext;
use crate::peer_connection::sdp::session_description::RTCSessionDescription;
use crate::peer_connection::sdp::{
extract_fingerprint, extract_ice_details, get_application_media_section_max_message_size,
get_application_media_section_sctp_port, get_mid_value, get_peer_direction,
has_ice_trickle_option, is_lite_set, sdp_type::RTCSdpType, update_sdp_origin,
};
use crate::peer_connection::state::RTCIceGatheringState;
use crate::peer_connection::state::ice_connection_state::RTCIceConnectionState;
use crate::peer_connection::state::peer_connection_state::{
NegotiationNeededState, RTCPeerConnectionState,
};
use crate::peer_connection::state::signaling_state::{RTCSignalingState, StateChangeOp};
use crate::peer_connection::transport::dtls::RTCDtlsTransport;
use crate::peer_connection::transport::dtls::fingerprint::RTCDtlsFingerprint;
use crate::peer_connection::transport::dtls::parameters::DTLSParameters;
use crate::peer_connection::transport::dtls::role::{
DEFAULT_DTLS_ROLE_ANSWER, DEFAULT_DTLS_ROLE_OFFER, RTCDtlsRole,
};
use crate::peer_connection::transport::ice::RTCIceTransport;
use crate::peer_connection::transport::ice::candidate::RTCIceCandidateInit;
use crate::peer_connection::transport::ice::parameters::RTCIceParameters;
use crate::peer_connection::transport::ice::role::RTCIceRole;
use crate::peer_connection::transport::sctp::RTCSctpTransport;
use crate::peer_connection::transport::sctp::capabilities::SCTPTransportCapabilities;
use crate::rtp_transceiver::direction::RTCRtpTransceiverDirection;
use crate::rtp_transceiver::rtp_receiver::RTCRtpReceiver;
use crate::rtp_transceiver::rtp_sender::internal::RTCRtpSenderInternal;
use crate::rtp_transceiver::rtp_sender::rtp_codec::{
CodecMatch, RtpCodecKind, encoding_parameters_fuzzy_search,
};
use crate::rtp_transceiver::rtp_sender::{
RTCRtpCodingParameters, RTCRtpEncodingParameters, RTCRtpSender,
};
use crate::rtp_transceiver::{
RTCRtpReceiverId, RTCRtpSenderId, RTCRtpTransceiver, RTCRtpTransceiverId, RTCRtpTransceiverInit,
};
use crate::statistics::StatsSelector;
use crate::statistics::accumulator::RTCStatsAccumulator;
use crate::statistics::report::RTCStatsReport;
use ::sdp::description::session::Origin;
use ::sdp::util::ConnectionRole;
use ice::AgentConfig;
use ice::candidate::{Candidate, unmarshal_candidate};
use interceptor::{Interceptor, NoopInterceptor, Registry};
use sdp::MEDIA_SECTION_APPLICATION;
use shared::error::{Error, Result};
use shared::util::math_rand_alpha;
use std::collections::HashMap;
use std::time::Instant;
pub struct RTCPeerConnectionBuilder<I = NoopInterceptor>
where
I: Interceptor,
{
configuration: RTCConfiguration,
media_engine: MediaEngine,
setting_engine: SettingEngine,
interceptor: I,
}
impl Default for RTCPeerConnectionBuilder<NoopInterceptor> {
fn default() -> Self {
Self {
configuration: RTCConfiguration::default(),
media_engine: MediaEngine::default(),
setting_engine: SettingEngine::default(),
interceptor: NoopInterceptor::new(),
}
}
}
impl RTCPeerConnectionBuilder<NoopInterceptor> {
pub fn new() -> Self {
Self::default()
}
}
impl<I> RTCPeerConnectionBuilder<I>
where
I: Interceptor,
{
pub fn with_configuration(mut self, configuration: RTCConfiguration) -> Self {
self.configuration = configuration;
self
}
pub fn with_media_engine(mut self, media_engine: MediaEngine) -> Self {
self.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<P>(
self,
interceptor_registry: Registry<P>,
) -> RTCPeerConnectionBuilder<P>
where
P: Interceptor,
{
RTCPeerConnectionBuilder {
configuration: self.configuration,
media_engine: self.media_engine,
setting_engine: self.setting_engine,
interceptor: interceptor_registry.build(),
}
}
pub fn build(self) -> Result<RTCPeerConnection<I>> {
RTCPeerConnection::new(
self.configuration,
self.media_engine,
self.setting_engine,
self.interceptor,
)
}
}
pub struct RTCPeerConnection<I = NoopInterceptor>
where
I: Interceptor,
{
pub(crate) configuration: RTCConfiguration,
pub(crate) media_engine: MediaEngine,
pub(crate) setting_engine: SettingEngine,
pub(crate) interceptor: I,
local_description: Option<RTCSessionDescription>,
current_local_description: Option<RTCSessionDescription>,
pending_local_description: Option<RTCSessionDescription>,
remote_description: Option<RTCSessionDescription>,
current_remote_description: Option<RTCSessionDescription>,
pending_remote_description: Option<RTCSessionDescription>,
pub(crate) signaling_state: RTCSignalingState,
pub(crate) peer_connection_state: RTCPeerConnectionState,
can_trickle_ice_candidates: Option<bool>,
pub(crate) pipeline_context: PipelineContext,
pub(crate) data_channels: HashMap<RTCDataChannelId, RTCDataChannelInternal>,
pub(super) rtp_transceivers: Vec<RTCRtpTransceiver<I>>,
greater_mid: isize,
sdp_origin: Origin,
last_offer: String,
last_answer: String,
ice_restart_requested: Option<RTCOfferOptions>,
negotiation_needed_state: NegotiationNeededState,
is_negotiation_ongoing: bool,
}
impl<I> RTCPeerConnection<I>
where
I: Interceptor,
{
pub fn create_offer(
&mut self,
mut options: Option<RTCOfferOptions>,
) -> Result<RTCSessionDescription> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
let is_ice_restart_requested = self
.ice_restart_requested
.take()
.is_some_and(|options| options.ice_restart)
|| options.take().is_some_and(|options| options.ice_restart);
if is_ice_restart_requested {
self.ice_restart()?;
}
if let Some(d) = self.current_remote_description.as_ref()
&& let Some(parsed) = &d.parsed
{
for media in &parsed.media_descriptions {
if let Some(mid) = get_mid_value(media) {
if mid.is_empty() {
continue;
}
let numeric_mid = match mid.parse::<isize>() {
Ok(n) => n,
Err(_) => continue,
};
if numeric_mid > self.greater_mid {
self.greater_mid = numeric_mid;
}
}
}
}
for transceiver in &mut self.rtp_transceivers {
if let Some(mid) = transceiver.mid()
&& !mid.is_empty()
{
if let Ok(numeric_mid) = mid.parse::<isize>()
&& numeric_mid > self.greater_mid
{
self.greater_mid = numeric_mid;
}
} else {
self.greater_mid += 1;
transceiver.set_mid(format!("{}", self.greater_mid))?;
}
}
let mut d = if self.current_remote_description.is_none() {
self.generate_unmatched_sdp()?
} else {
self.generate_matched_sdp(
true,
DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
false,
)?
};
update_sdp_origin(&mut self.sdp_origin, &mut d);
let sdp = d.marshal();
let offer = RTCSessionDescription {
sdp_type: RTCSdpType::Offer,
sdp,
parsed: Some(d),
};
self.last_offer.clone_from(&offer.sdp);
Ok(offer)
}
pub fn create_answer(
&mut self,
_options: Option<RTCAnswerOptions>,
) -> Result<RTCSessionDescription> {
if self.remote_description().is_none() {
return Err(Error::ErrNoRemoteDescription);
}
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
if self.signaling_state != RTCSignalingState::HaveRemoteOffer
&& self.signaling_state != RTCSignalingState::HaveLocalPranswer
{
return Err(Error::ErrIncorrectSignalingState);
}
let mut connection_role = self.setting_engine.answering_dtls_role.to_connection_role();
if connection_role == ConnectionRole::Unspecified {
connection_role = DEFAULT_DTLS_ROLE_ANSWER.to_connection_role();
if let Some(remote_description) = self.remote_description()
&& let Some(parsed) = remote_description.parsed.as_ref()
&& is_lite_set(parsed)
&& !self.setting_engine.candidates.ice_lite
{
connection_role = RTCDtlsRole::Server.to_connection_role();
}
}
let mut d = self.generate_matched_sdp(
false,
connection_role,
self.setting_engine.ignore_rid_pause_for_recv,
)?;
update_sdp_origin(&mut self.sdp_origin, &mut d);
let sdp = d.marshal();
let answer = RTCSessionDescription {
sdp_type: RTCSdpType::Answer,
sdp,
parsed: Some(d),
};
self.last_answer.clone_from(&answer.sdp);
Ok(answer)
}
pub fn set_local_description(
&mut self,
mut local_description: RTCSessionDescription,
) -> Result<()> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
if local_description.sdp.is_empty() {
match local_description.sdp_type {
RTCSdpType::Answer | RTCSdpType::Pranswer => {
local_description.sdp.clone_from(&self.last_answer);
}
RTCSdpType::Offer => {
local_description.sdp.clone_from(&self.last_offer);
}
RTCSdpType::Rollback => {
}
_ => return Err(Error::ErrPeerConnSDPTypeInvalidValueSetLocalDescription),
}
}
if local_description.sdp_type != RTCSdpType::Rollback {
local_description.parsed = Some(local_description.unmarshal()?);
}
self.set_description(&local_description, StateChangeOp::SetLocal)?;
let we_answer = local_description.sdp_type == RTCSdpType::Answer;
if we_answer && let Some(parsed_local_description) = &local_description.parsed {
for media in &parsed_local_description.media_descriptions {
let mid_value = match get_mid_value(media) {
Some(mid) if !mid.is_empty() => mid,
_ => return Err(Error::ErrPeerConnLocalDescriptionWithoutMidValue),
};
if media.media_name.media == MEDIA_SECTION_APPLICATION {
continue;
}
let i = match RTCPeerConnection::find_by_mid(mid_value, &self.rtp_transceivers) {
Some(i) => i,
None => return Err(Error::ErrPeerConnTransceiverMidNil),
};
let kind = RtpCodecKind::from(media.media_name.media.as_str());
let mut direction = get_peer_direction(media);
if kind == RtpCodecKind::Unspecified
|| direction == RTCRtpTransceiverDirection::Unspecified
{
continue;
}
if direction == RTCRtpTransceiverDirection::Sendonly
&& self.rtp_transceivers[i].sender().is_none()
{
direction = RTCRtpTransceiverDirection::Inactive;
}
self.rtp_transceivers[i].set_current_direction(direction);
}
if let Some(remote_description) = self.remote_description().cloned()
&& let Some(parsed_remote_description) = remote_description.parsed.as_ref()
{
let (dtls_role, remote_caps, local_sctp_port, remote_sctp_port) = (
self.dtls_transport().role(),
SCTPTransportCapabilities {
max_message_size: get_application_media_section_max_message_size(
parsed_remote_description,
)
.unwrap_or(SctpMaxMessageSize::DEFAULT_MESSAGE_SIZE),
},
get_application_media_section_sctp_port(parsed_local_description)
.unwrap_or(5000),
get_application_media_section_sctp_port(parsed_remote_description)
.unwrap_or(5000),
);
self.sctp_transport_mut().start(
dtls_role,
remote_caps,
local_sctp_port,
remote_sctp_port,
)?;
self.start_rtp(remote_description)?;
}
}
Ok(())
}
pub fn local_description(&self) -> Option<&RTCSessionDescription> {
if self.pending_local_description.is_some() {
self.pending_local_description.as_ref()
} else {
self.current_local_description.as_ref()
}
}
pub fn current_local_description(&self) -> Option<&RTCSessionDescription> {
self.current_local_description.as_ref()
}
pub fn pending_local_description(&self) -> Option<&RTCSessionDescription> {
self.pending_local_description.as_ref()
}
pub fn can_trickle_ice_candidates(&self) -> Option<bool> {
self.can_trickle_ice_candidates
}
pub fn set_remote_description(
&mut self,
mut remote_description: RTCSessionDescription,
) -> Result<()> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
let is_renegotiation = self.current_remote_description.is_some();
if remote_description.sdp_type != RTCSdpType::Rollback {
remote_description.parsed = Some(remote_description.unmarshal()?);
}
self.set_description(&remote_description, StateChangeOp::SetRemote)?;
if let Some(parsed_remote_description) = &remote_description.parsed {
self.media_engine
.update_from_remote_description(parsed_remote_description)?;
let has_trickle_ice = has_ice_trickle_option(parsed_remote_description);
match remote_description.sdp_type {
RTCSdpType::Offer | RTCSdpType::Answer | RTCSdpType::Pranswer => {
self.can_trickle_ice_candidates = Some(has_trickle_ice);
}
_ => {
self.can_trickle_ice_candidates = None;
}
}
for transceiver in &mut self.rtp_transceivers {
if let Some(sender) = transceiver.sender_mut() {
let (is_rtx_enabled, is_fec_enabled) = (
self.media_engine
.is_rtx_enabled(sender.kind(), RTCRtpTransceiverDirection::Sendonly),
self.media_engine
.is_fec_enabled(sender.kind(), RTCRtpTransceiverDirection::Sendonly),
);
sender.configure_rtx_and_fec(is_rtx_enabled, is_fec_enabled);
}
}
let we_offer = remote_description.sdp_type == RTCSdpType::Answer;
let media_descriptions = self
.remote_description()
.as_ref()
.and_then(|r| r.parsed.as_ref())
.map(|parsed| parsed.media_descriptions.clone());
if let Some(media_descriptions) = media_descriptions {
if !we_offer {
for media in &media_descriptions {
let mid_value = match get_mid_value(media) {
Some(mid) if !mid.is_empty() => mid,
_ => return Err(Error::ErrPeerConnRemoteDescriptionWithoutMidValue),
};
if media.media_name.media == MEDIA_SECTION_APPLICATION {
continue;
}
let kind = RtpCodecKind::from(media.media_name.media.as_str());
let direction = get_peer_direction(media);
if kind == RtpCodecKind::Unspecified
|| direction == RTCRtpTransceiverDirection::Unspecified
{
continue;
}
let transceiver = if let Some(i) =
RTCPeerConnection::find_by_mid(mid_value, &self.rtp_transceivers)
{
if direction == RTCRtpTransceiverDirection::Inactive {
self.rtp_transceivers[i]
.stop(&self.media_engine, &mut self.interceptor)?;
}
Some(&mut self.rtp_transceivers[i])
} else {
RTCPeerConnection::satisfy_type_and_direction(
kind,
direction,
&mut self.rtp_transceivers,
)
};
if let Some(transceiver) = transceiver {
if direction == RTCRtpTransceiverDirection::Recvonly {
if transceiver.direction() == RTCRtpTransceiverDirection::Sendrecv {
transceiver.set_direction(RTCRtpTransceiverDirection::Sendonly);
} else if transceiver.direction()
== RTCRtpTransceiverDirection::Recvonly
{
transceiver.set_direction(RTCRtpTransceiverDirection::Inactive);
}
} else if direction == RTCRtpTransceiverDirection::Sendrecv {
if transceiver.direction() == RTCRtpTransceiverDirection::Sendonly {
transceiver.set_direction(RTCRtpTransceiverDirection::Sendrecv);
} else if transceiver.direction()
== RTCRtpTransceiverDirection::Inactive
{
transceiver.set_direction(RTCRtpTransceiverDirection::Recvonly);
}
} else if direction == RTCRtpTransceiverDirection::Sendonly
&& transceiver.direction() == RTCRtpTransceiverDirection::Inactive
{
transceiver.set_direction(RTCRtpTransceiverDirection::Recvonly);
}
transceiver.set_codec_preferences_from_remote_description(
media,
&self.media_engine,
)?;
if transceiver.mid().is_none() {
transceiver.set_mid(mid_value.to_string())?;
}
} else {
let local_direction =
if direction == RTCRtpTransceiverDirection::Recvonly {
RTCRtpTransceiverDirection::Sendonly
} else {
RTCRtpTransceiverDirection::Recvonly
};
let mut transceiver = RTCRtpTransceiver::new(
kind,
None,
RTCRtpTransceiverInit {
direction: local_direction,
streams: vec![],
send_encodings: vec![],
},
);
transceiver.set_codec_preferences_from_remote_description(
media,
&self.media_engine,
)?;
if transceiver.mid().is_none() {
transceiver.set_mid(mid_value.to_string())?;
}
self.add_rtp_transceiver(transceiver);
}
}
} else {
for media in &media_descriptions {
let mid_value = match get_mid_value(media) {
Some(mid) if !mid.is_empty() => mid,
_ => return Err(Error::ErrPeerConnRemoteDescriptionWithoutMidValue),
};
if media.media_name.media == MEDIA_SECTION_APPLICATION {
continue;
}
let kind = RtpCodecKind::from(media.media_name.media.as_str());
let mut direction = get_peer_direction(media);
if kind == RtpCodecKind::Unspecified
|| direction == RTCRtpTransceiverDirection::Unspecified
{
continue;
}
let transceiver = if let Some(i) =
RTCPeerConnection::find_by_mid(mid_value, &self.rtp_transceivers)
{
&mut self.rtp_transceivers[i]
} else {
return Err(Error::ErrPeerConnTransceiverMidNil);
};
if direction == RTCRtpTransceiverDirection::Sendonly {
direction = RTCRtpTransceiverDirection::Recvonly;
} else if direction == RTCRtpTransceiverDirection::Recvonly {
direction = RTCRtpTransceiverDirection::Sendonly;
}
transceiver.set_current_direction(direction);
transceiver.set_codec_preferences_from_remote_description(
media,
&self.media_engine,
)?;
}
}
}
let (remote_ufrag, remote_pwd, candidates) =
extract_ice_details(parsed_remote_description)?;
if is_renegotiation
&& self
.ice_transport()
.have_remote_credentials_change(&remote_ufrag, &remote_pwd)
{
if !we_offer {
self.ice_restart()?;
}
self.ice_transport_mut()
.set_remote_credentials(remote_ufrag.clone(), remote_pwd.clone())?;
}
for candidate in candidates {
self.ice_transport_mut().add_remote_candidate(candidate)?;
}
if !is_renegotiation {
let remote_is_lite = is_lite_set(parsed_remote_description);
let (remote_fingerprint, remote_fingerprint_hash) =
extract_fingerprint(parsed_remote_description)?;
let local_ice_role = if (we_offer
&& remote_is_lite == self.setting_engine.candidates.ice_lite)
|| (remote_is_lite && !self.setting_engine.candidates.ice_lite)
{
RTCIceRole::Controlling
} else {
RTCIceRole::Controlled
};
let remote_dtls_role = RTCDtlsRole::from(parsed_remote_description);
log::trace!(
"start_transports: local_ice_role={local_ice_role}, remote_dtls_role={remote_dtls_role}"
);
self.start_transports(
local_ice_role,
RTCIceParameters {
username_fragment: remote_ufrag,
password: remote_pwd,
ice_lite: remote_is_lite,
},
DTLSParameters {
role: remote_dtls_role,
fingerprints: vec![RTCDtlsFingerprint {
algorithm: remote_fingerprint_hash,
value: remote_fingerprint,
}],
},
)?;
}
if we_offer
&& let Some(parsed_local_description) = self
.current_local_description
.as_ref()
.and_then(|desc| desc.parsed.as_ref())
{
let (dtls_role, remote_caps, local_sctp_port, remote_sctp_port) = (
self.dtls_transport().role(),
SCTPTransportCapabilities {
max_message_size: get_application_media_section_max_message_size(
parsed_remote_description,
)
.unwrap_or(SctpMaxMessageSize::DEFAULT_MESSAGE_SIZE),
},
get_application_media_section_sctp_port(parsed_local_description)
.unwrap_or(5000),
get_application_media_section_sctp_port(parsed_remote_description)
.unwrap_or(5000),
);
self.sctp_transport_mut().start(
dtls_role,
remote_caps,
local_sctp_port,
remote_sctp_port,
)?;
self.start_rtp(remote_description)?;
}
}
Ok(())
}
pub fn remote_description(&self) -> Option<&RTCSessionDescription> {
if self.pending_remote_description.is_some() {
self.pending_remote_description.as_ref()
} else {
self.current_remote_description.as_ref()
}
}
pub fn current_remote_description(&self) -> Option<&RTCSessionDescription> {
self.current_remote_description.as_ref()
}
pub fn pending_remote_description(&self) -> Option<&RTCSessionDescription> {
self.pending_remote_description.as_ref()
}
pub fn add_remote_candidate(&mut self, remote_candidate: RTCIceCandidateInit) -> Result<()> {
if self.remote_description().is_none() {
return Err(Error::ErrNoRemoteDescription);
}
let candidate_value = match remote_candidate.candidate.strip_prefix("candidate:") {
Some(s) => s,
None => remote_candidate.candidate.as_str(),
};
if !candidate_value.is_empty() {
self.add_ice_remote_candidate(candidate_value)?;
}
Ok(())
}
pub fn add_local_candidate(&mut self, local_candidate: RTCIceCandidateInit) -> Result<()> {
let candidate_value = match local_candidate.candidate.strip_prefix("candidate:") {
Some(s) => s,
None => local_candidate.candidate.as_str(),
};
if !candidate_value.is_empty() {
self.add_ice_local_candidate(candidate_value, local_candidate.url.as_deref())?;
} else {
self.pipeline_context.event_outs.push_back(
RTCPeerConnectionEvent::OnIceGatheringStateChangeEvent(
RTCIceGatheringState::Complete,
),
);
}
Ok(())
}
pub fn restart_ice(&mut self) {
self.ice_restart_requested = Some(RTCOfferOptions { ice_restart: true });
}
pub fn get_configuration(&self) -> &RTCConfiguration {
&self.configuration
}
pub fn set_configuration(&mut self, configuration: RTCConfiguration) -> Result<()> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
if !configuration.peer_identity.is_empty() {
if configuration.peer_identity != self.configuration.peer_identity {
return Err(Error::ErrModifyingPeerIdentity);
}
self.configuration.peer_identity = configuration.peer_identity;
}
if !configuration.certificates.is_empty() {
if configuration.certificates.len() != self.configuration.certificates.len() {
return Err(Error::ErrModifyingCertificates);
}
self.configuration.certificates = configuration.certificates;
}
if configuration.bundle_policy != self.configuration.bundle_policy {
return Err(Error::ErrModifyingBundlePolicy);
}
self.configuration.bundle_policy = configuration.bundle_policy;
if configuration.rtcp_mux_policy != self.configuration.rtcp_mux_policy {
return Err(Error::ErrModifyingRTCPMuxPolicy);
}
self.configuration.rtcp_mux_policy = configuration.rtcp_mux_policy;
if configuration.ice_candidate_pool_size != 0 {
if self.configuration.ice_candidate_pool_size != configuration.ice_candidate_pool_size
&& self.local_description().is_some()
{
return Err(Error::ErrModifyingICECandidatePoolSize);
}
self.configuration.ice_candidate_pool_size = configuration.ice_candidate_pool_size;
}
self.configuration.ice_transport_policy = configuration.ice_transport_policy;
if !configuration.ice_servers.is_empty() {
for server in &configuration.ice_servers {
server.validate()?;
}
self.configuration.ice_servers = configuration.ice_servers
}
Ok(())
}
pub fn create_data_channel(
&mut self,
label: &str,
options: Option<RTCDataChannelInit>,
) -> Result<RTCDataChannel<'_, I>>
where
I: Interceptor,
{
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
let mut params = DataChannelParameters {
label: label.to_owned(),
..Default::default()
};
let mut id = self.generate_data_channel_id()?;
if let Some(options) = options {
if options.max_packet_life_time.is_some() && options.max_retransmits.is_some() {
return Err(Error::ErrRetransmitsOrPacketLifeTime);
}
params.ordered = options.ordered;
params.max_packet_life_time = options.max_packet_life_time;
params.max_retransmits = options.max_retransmits;
params.protocol = options.protocol;
if params.protocol.len() > 65535 {
return Err(Error::ErrProtocolTooLarge);
}
params.negotiated = options.negotiated;
if let Some(negotiated_id) = ¶ms.negotiated {
id = *negotiated_id;
}
}
let data_channel = RTCDataChannelInternal::new(id, params);
self.data_channels.insert(id, data_channel);
self.trigger_negotiation_needed();
Ok(RTCDataChannel {
id,
peer_connection: self,
})
}
pub fn get_senders(&self) -> impl Iterator<Item = RTCRtpSenderId> + use<'_, I> {
self.rtp_transceivers
.iter()
.enumerate()
.filter(|(_, transceiver)| transceiver.direction().has_send())
.map(|(id, _)| RTCRtpSenderId(id))
}
pub fn get_receivers(&self) -> impl Iterator<Item = RTCRtpReceiverId> + use<'_, I> {
self.rtp_transceivers
.iter()
.enumerate()
.filter(|(_, transceiver)| transceiver.direction().has_recv())
.map(|(id, _)| RTCRtpReceiverId(id))
}
pub fn get_transceivers(&self) -> impl Iterator<Item = RTCRtpTransceiverId> {
0..self.rtp_transceivers.len()
}
pub fn add_track(&mut self, track: MediaStreamTrack) -> Result<RTCRtpSenderId> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
let send_encodings = self.send_encodings_from_track(&track);
for (id, transceiver) in self.rtp_transceivers.iter_mut().enumerate() {
if !transceiver.stopped()
&& transceiver.kind() == track.kind()
&& transceiver.sender().is_none()
{
let mut sender =
RTCRtpSenderInternal::new(track.kind(), track, vec![], send_encodings);
sender.set_codec_preferences(transceiver.get_codec_preferences().to_vec());
transceiver.sender_mut().replace(sender);
transceiver.set_direction(RTCRtpTransceiverDirection::from_send_recv(
true,
transceiver.direction().has_recv(),
));
self.trigger_negotiation_needed();
return Ok(RTCRtpSenderId(id));
}
}
let transceiver = self.new_transceiver_from_track(
track,
RTCRtpTransceiverInit {
direction: RTCRtpTransceiverDirection::Sendrecv,
streams: vec![],
send_encodings,
},
)?;
Ok(RTCRtpSenderId(self.add_rtp_transceiver(transceiver)))
}
pub fn remove_track(&mut self, sender_id: RTCRtpSenderId) -> Result<()> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
if sender_id.0 >= self.rtp_transceivers.len() {
return Err(Error::ErrRTPSenderNotExisted);
}
let has_recv = self.rtp_transceivers[sender_id.0].direction().has_recv();
self.rtp_transceivers[sender_id.0]
.set_direction(RTCRtpTransceiverDirection::from_send_recv(false, has_recv));
if let Some(sender) = self.rtp_transceivers[sender_id.0].sender_mut()
&& sender
.stop(&self.media_engine, &mut self.interceptor)
.is_ok()
{
self.trigger_negotiation_needed();
}
self.rtp_transceivers[sender_id.0].sender_mut().take();
Ok(())
}
pub fn add_transceiver_from_track(
&mut self,
track: MediaStreamTrack,
init: Option<RTCRtpTransceiverInit>,
) -> Result<RTCRtpTransceiverId> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
if let Some(init) = init.as_ref()
&& !init.direction.has_send()
{
return Err(Error::ErrInvalidDirection);
}
let transceiver = self.new_transceiver_from_track(
track,
if let Some(init) = init {
init
} else {
RTCRtpTransceiverInit {
direction: RTCRtpTransceiverDirection::Sendrecv,
streams: vec![],
send_encodings: vec![],
}
},
)?;
Ok(self.add_rtp_transceiver(transceiver))
}
pub fn add_transceiver_from_kind(
&mut self,
kind: RtpCodecKind,
init: Option<RTCRtpTransceiverInit>,
) -> Result<RTCRtpTransceiverId> {
if self.peer_connection_state == RTCPeerConnectionState::Closed {
return Err(Error::ErrConnectionClosed);
}
let (direction, streams, send_encodings) = if let Some(init) = init {
if init.direction.has_send() && init.send_encodings.is_empty() {
return Err(Error::ErrInvalidDirection);
}
(init.direction, init.streams, init.send_encodings)
} else {
(RTCRtpTransceiverDirection::Recvonly, vec![], vec![])
};
let transceiver = match direction {
RTCRtpTransceiverDirection::Sendonly | RTCRtpTransceiverDirection::Sendrecv => {
let codecs = self.media_engine.get_codecs_by_kind(kind);
let (encoding, code_match_result) =
encoding_parameters_fuzzy_search(&send_encodings, &codecs);
if code_match_result != CodecMatch::None {
if encoding.rtp_coding_parameters.rid.is_empty()
&& encoding.rtp_coding_parameters.ssrc.is_none()
{
return Err(Error::ErrRTPSenderNoBaseEncoding);
}
let track = MediaStreamTrack::new(
math_rand_alpha(16), math_rand_alpha(16), math_rand_alpha(16), kind,
vec![RTCRtpEncodingParameters {
rtp_coding_parameters: RTCRtpCodingParameters {
rid: encoding.rtp_coding_parameters.rid,
ssrc: if let Some(ssrc) = encoding.rtp_coding_parameters.ssrc {
Some(ssrc)
} else {
Some(rand::random::<u32>())
},
rtx: None,
fec: None,
},
codec: encoding.codec,
..Default::default()
}],
);
self.new_transceiver_from_track(
track,
RTCRtpTransceiverInit {
direction,
streams,
send_encodings,
},
)?
} else {
return Err(Error::ErrRTPSenderNoBaseEncoding);
}
}
RTCRtpTransceiverDirection::Recvonly => RTCRtpTransceiver::new(
kind,
None,
RTCRtpTransceiverInit {
direction,
streams: vec![],
send_encodings: vec![],
},
),
_ => return Err(Error::ErrPeerConnAddTransceiverFromKindSupport),
};
Ok(self.add_rtp_transceiver(transceiver))
}
pub fn data_channel(&mut self, id: RTCDataChannelId) -> Option<RTCDataChannel<'_, I>>
where
I: Interceptor,
{
if self.data_channels.contains_key(&id) {
Some(RTCDataChannel {
id,
peer_connection: self,
})
} else {
None
}
}
pub fn rtp_sender(&mut self, id: RTCRtpSenderId) -> Option<RTCRtpSender<'_, I>>
where
I: Interceptor,
{
if id.0 < self.rtp_transceivers.len()
&& self.rtp_transceivers[id.0].direction().has_send()
&& self.rtp_transceivers[id.0].sender().is_some()
{
Some(RTCRtpSender {
id,
peer_connection: self,
})
} else {
None
}
}
pub fn rtp_receiver(&mut self, id: RTCRtpReceiverId) -> Option<RTCRtpReceiver<'_, I>>
where
I: Interceptor,
{
if id.0 < self.rtp_transceivers.len()
&& self.rtp_transceivers[id.0].direction().has_recv()
&& self.rtp_transceivers[id.0].receiver().is_some()
{
Some(RTCRtpReceiver {
id,
peer_connection: self,
})
} else {
None
}
}
pub fn get_stats(&mut self, now: Instant, selector: StatsSelector) -> RTCStatsReport {
self.update_ice_agent_stats();
self.update_codec_stats();
self.pipeline_context
.stats
.snapshot_with_selector(now, selector)
}
}