///|
pub using @transport {
type ClockSample,
type ConnectionId,
type Duration,
type EcnCodepoint,
type InboundDatagram,
type Instant,
type IoAction,
type IoEvent,
type IpAddress,
type ListenerId,
type ManualClock,
type OutboundDatagram,
type SocketAddress,
type TransportContext,
type TransportProtocol,
type WallTime,
}
///|
pub using @sdp {type SdpType, type SessionDescription, type SignalingState}
///|
pub using @ice {
type CandidateType,
type IceCandidate,
type IceConnectionState,
type IceGatheringState,
type IceRole,
type IceTransportPolicy,
type TcpCandidateType,
}
///|
pub using @turn {type TurnTransport}
///|
pub using @datachannel {
type DataChannel,
type DataChannelConfig,
type DataChannelMessage,
type DataChannelState,
}
///|
pub using @media {
type CodecCapability,
type EncodedFrameContext,
type EncodedTransform,
type EncodedTransformPipeline,
type MediaKind,
type MediaTrack,
type RtpEncodingParameters,
type RtpParameters,
type RtpReceiver,
type RtpSender,
type RtpTransceiver,
type TransformDirection,
type TransceiverDirection,
}
///|
pub using @interceptor {type Context, type Direction, type Pipeline, type Stage}
///|
pub(all) suberror RtcError {
InvalidConfiguration(String)
InvalidState(String)
BackpressureExceeded
CryptoUnavailable(String)
Sdp(@sdp.SdpError)
Stun(@stun.StunError)
Turn(@turn.TurnError)
Mdns(@mdns.MdnsError)
Ice(@ice.IceError)
Dtls(@dtls.DtlsError)
Srtp(@srtp.SrtpError)
Sctp(@sctp.SctpError)
DataChannel(@datachannel.DataChannelError)
Interceptor(@interceptor.InterceptorError)
Closed
} derive(Debug, Eq)
///|
pub(all) enum PeerConnectionState {
New
Connecting
Connected
Disconnected
Failed
Closed
} derive(Debug, Eq)
///|
pub(all) enum OfferCollisionPolicy {
Polite
Impolite
} derive(Debug, Eq)
///|
pub(all) struct IceServer {
urls : Array[String]
username : String?
credential : String?
} derive(Debug, Eq)
///|
pub fn IceServer::new(
urls~ : Array[String],
username? : String,
credential? : String,
) -> IceServer raise RtcError {
if urls.is_empty() || urls.any(url => url.is_empty()) {
raise InvalidConfiguration("ICE server URLs must be nonempty")
}
if (username is Some(_)) != (credential is Some(_)) {
raise InvalidConfiguration(
"ICE server username and credential must appear together",
)
}
{ urls: urls.copy(), username, credential, }
}
///|
pub fn IceServer::urls(self : IceServer) -> Array[String] {
self.urls.copy()
}
///|
pub fn IceServer::username(self : IceServer) -> String? {
self.username
}
///|
pub fn IceServer::credential(self : IceServer) -> String? {
self.credential
}
///|
fn settings_duration(
milliseconds : Int64,
name : String,
) -> Duration raise RtcError {
Duration::milliseconds(milliseconds) catch {
_ => raise InvalidConfiguration("\{name} is outside the supported range")
}
}
///|
pub struct Settings {
ice_credentials : @ice.IceCredentials?
ice_check_interval : Duration
ice_initial_rto : Duration
ice_reliable_timeout : Duration
ice_max_retransmissions : Int
ice_disconnected_timeout : Duration
ice_failed_timeout : Duration
ice_keepalive_interval : Duration
mdns_retry_interval : Duration
mdns_query_timeout : Duration
mdns_local_name : String?
answering_dtls_role : @dtls.Role?
verify_peer_fingerprint : Bool
dtls_certificate_key_type : @dtls.CertificateKeyType
dtls_cipher_suites : Array[@dtls.CipherSuite]
dtls_psk : Bytes?
dtls_psk_identity : Bytes?
dtls_srtp_profiles : Array[@dtls.SrtpProtectionProfile]
dtls_flight_interval : Duration
dtls_mtu : Int
dtls_replay_window : Int
srtp_replay_window : Int
srtcp_replay_window : Int
sctp_max_payload_size : Int
sctp_send_buffer_size : UInt64
sctp_receive_buffer_size : UInt
sctp_initial_rto : Duration
receive_mtu : Int
ignore_rid_pause_for_recv : Bool
write_ssrc_attributes_for_simulcast : Bool
}
///|
pub fn Settings::new(
ice_credentials? : @ice.IceCredentials,
ice_check_interval? : Duration,
ice_initial_rto? : Duration,
ice_reliable_timeout? : Duration,
ice_max_retransmissions? : Int = 7,
ice_disconnected_timeout? : Duration,
ice_failed_timeout? : Duration,
ice_keepalive_interval? : Duration,
mdns_retry_interval? : Duration,
mdns_query_timeout? : Duration,
mdns_local_name? : String,
answering_dtls_role? : @dtls.Role,
verify_peer_fingerprint? : Bool = true,
dtls_certificate_key_type? : @dtls.CertificateKeyType = EcdsaCertificate,
dtls_cipher_suites? : Array[@dtls.CipherSuite] = [],
dtls_psk? : Bytes,
dtls_psk_identity? : Bytes,
dtls_srtp_profiles? : Array[@dtls.SrtpProtectionProfile] = [
@dtls.SrtpProtectionProfile::aes128_cm_hmac_sha1_80(),
],
dtls_flight_interval? : Duration,
dtls_mtu? : Int = 1200,
dtls_replay_window? : Int = 64,
srtp_replay_window? : Int = 64,
srtcp_replay_window? : Int = 64,
sctp_max_payload_size? : Int = 1000,
sctp_send_buffer_size? : UInt64 = 1048576UL,
sctp_receive_buffer_size? : UInt = 1048576U,
sctp_initial_rto? : Duration,
receive_mtu? : Int = 1460,
ignore_rid_pause_for_recv? : Bool = false,
write_ssrc_attributes_for_simulcast? : Bool = true,
) -> Settings raise RtcError {
let ice_check_interval = ice_check_interval.unwrap_or(
settings_duration(50L, "ICE check interval"),
)
let ice_initial_rto = ice_initial_rto.unwrap_or(
settings_duration(500L, "ICE initial RTO"),
)
let ice_reliable_timeout = ice_reliable_timeout.unwrap_or(
settings_duration(39500L, "ICE reliable transport timeout"),
)
let ice_disconnected_timeout = ice_disconnected_timeout.unwrap_or(
settings_duration(5000L, "ICE disconnected timeout"),
)
let ice_failed_timeout = ice_failed_timeout.unwrap_or(
settings_duration(25000L, "ICE failed timeout"),
)
let ice_keepalive_interval = ice_keepalive_interval.unwrap_or(
settings_duration(2000L, "ICE keepalive interval"),
)
let mdns_retry_interval = mdns_retry_interval.unwrap_or(
settings_duration(1000L, "mDNS retry interval"),
)
let mdns_query_timeout = mdns_query_timeout.unwrap_or(
settings_duration(5000L, "mDNS query timeout"),
)
let dtls_flight_interval = dtls_flight_interval.unwrap_or(
settings_duration(1000L, "DTLS flight interval"),
)
let sctp_initial_rto = sctp_initial_rto.unwrap_or(
settings_duration(1000L, "SCTP initial RTO"),
)
if ice_check_interval.as_milliseconds() <= 0L ||
ice_initial_rto.as_milliseconds() <= 0L ||
ice_reliable_timeout.as_milliseconds() <= 0L ||
ice_disconnected_timeout.as_milliseconds() <= 0L ||
ice_failed_timeout.as_milliseconds() <= 0L ||
ice_keepalive_interval.as_milliseconds() <= 0L ||
mdns_retry_interval.as_milliseconds() <= 0L ||
mdns_query_timeout.as_milliseconds() <= 0L ||
dtls_flight_interval.as_milliseconds() <= 0L ||
sctp_initial_rto.as_milliseconds() <= 0L {
raise InvalidConfiguration("settings durations must be positive")
}
if ice_max_retransmissions < 0 || ice_max_retransmissions > 32 {
raise InvalidConfiguration(
"ICE maximum retransmissions must be between 0 and 32",
)
}
match mdns_local_name {
Some(name) =>
if name.is_empty() || !name.to_lower().has_suffix(".local") {
raise InvalidConfiguration(
"the static mDNS local name must end in .local",
)
}
None => ()
}
match answering_dtls_role {
Some(Auto) =>
raise InvalidConfiguration("the answering DTLS role cannot be Auto")
_ => ()
}
if (dtls_psk is Some(_)) != (dtls_psk_identity is Some(_)) {
raise InvalidConfiguration("DTLS PSK and PSK identity must appear together")
}
match (dtls_psk, dtls_psk_identity) {
(Some(key), Some(identity)) =>
if key.is_empty() || identity.is_empty() {
raise InvalidConfiguration("DTLS PSK and PSK identity must be nonempty")
}
_ => ()
}
let dtls_cipher_suites = if dtls_cipher_suites.is_empty() {
if dtls_psk is Some(_) {
[@dtls.PskAes128GcmSha256, PskAes128Ccm, PskAes128Ccm8]
} else {
match dtls_certificate_key_type {
EcdsaCertificate =>
[
EcdheEcdsaAes128GcmSha256,
EcdheEcdsaAes256CbcSha,
EcdheEcdsaChacha20Poly1305Sha256,
EcdheEcdsaAes128Ccm,
EcdheEcdsaAes128Ccm8,
]
RsaCertificate =>
[
EcdheRsaAes128GcmSha256,
EcdheRsaAes256CbcSha,
EcdheRsaChacha20Poly1305Sha256,
]
}
}
} else {
dtls_cipher_suites.copy()
}
let seen_cipher_suites : Array[@dtls.CipherSuite] = []
for suite in dtls_cipher_suites {
if seen_cipher_suites.contains(suite) {
raise InvalidConfiguration("DTLS cipher suites must be unique")
}
seen_cipher_suites.push(suite)
if suite.is_psk() != (dtls_psk is Some(_)) {
raise InvalidConfiguration(
"DTLS PSK credentials and cipher suites do not match",
)
}
if !suite.is_psk() {
let certificate_matches = match dtls_certificate_key_type {
EcdsaCertificate => !suite.uses_rsa()
RsaCertificate => suite.uses_rsa()
}
if !certificate_matches {
raise InvalidConfiguration(
"DTLS certificate key type and cipher suites do not match",
)
}
}
}
let seen_srtp_profiles : Array[@dtls.SrtpProtectionProfile] = []
for profile in dtls_srtp_profiles {
if seen_srtp_profiles.contains(profile) {
raise InvalidConfiguration("DTLS SRTP profiles must be unique")
}
seen_srtp_profiles.push(profile)
}
if dtls_mtu < 256 || dtls_mtu > 65535 {
raise InvalidConfiguration("DTLS MTU must be between 256 and 65535")
}
if dtls_replay_window < 1 ||
dtls_replay_window > 64 ||
srtp_replay_window < 1 ||
srtp_replay_window > 64 ||
srtcp_replay_window < 1 ||
srtcp_replay_window > 64 {
raise InvalidConfiguration(
"replay protection windows must be between 1 and 64",
)
}
if sctp_max_payload_size < 1 || sctp_max_payload_size > 0xffef {
raise InvalidConfiguration("SCTP payload size is outside its wire bounds")
}
if sctp_send_buffer_size == 0UL {
raise InvalidConfiguration("SCTP send buffer size must be nonzero")
}
if sctp_receive_buffer_size < 1500U {
raise InvalidConfiguration(
"SCTP receive buffer must be at least 1500 bytes",
)
}
if receive_mtu < 256 || receive_mtu > 65535 {
raise InvalidConfiguration("receive MTU must be between 256 and 65535")
}
{
ice_credentials,
ice_check_interval,
ice_initial_rto,
ice_reliable_timeout,
ice_max_retransmissions,
ice_disconnected_timeout,
ice_failed_timeout,
ice_keepalive_interval,
mdns_retry_interval,
mdns_query_timeout,
mdns_local_name,
answering_dtls_role,
verify_peer_fingerprint,
dtls_certificate_key_type,
dtls_cipher_suites,
dtls_psk,
dtls_psk_identity,
dtls_srtp_profiles: dtls_srtp_profiles.copy(),
dtls_flight_interval,
dtls_mtu,
dtls_replay_window,
srtp_replay_window,
srtcp_replay_window,
sctp_max_payload_size,
sctp_send_buffer_size,
sctp_receive_buffer_size,
sctp_initial_rto,
receive_mtu,
ignore_rid_pause_for_recv,
write_ssrc_attributes_for_simulcast,
}
}
///|
pub fn Settings::ice_credentials(self : Settings) -> @ice.IceCredentials? {
self.ice_credentials
}
///|
pub fn Settings::ice_check_interval(self : Settings) -> Duration {
self.ice_check_interval
}
///|
pub fn Settings::ice_initial_rto(self : Settings) -> Duration {
self.ice_initial_rto
}
///|
pub fn Settings::ice_reliable_timeout(self : Settings) -> Duration {
self.ice_reliable_timeout
}
///|
pub fn Settings::ice_max_retransmissions(self : Settings) -> Int {
self.ice_max_retransmissions
}
///|
pub fn Settings::ice_disconnected_timeout(self : Settings) -> Duration {
self.ice_disconnected_timeout
}
///|
pub fn Settings::ice_failed_timeout(self : Settings) -> Duration {
self.ice_failed_timeout
}
///|
pub fn Settings::ice_keepalive_interval(self : Settings) -> Duration {
self.ice_keepalive_interval
}
///|
pub fn Settings::mdns_retry_interval(self : Settings) -> Duration {
self.mdns_retry_interval
}
///|
pub fn Settings::mdns_query_timeout(self : Settings) -> Duration {
self.mdns_query_timeout
}
///|
pub fn Settings::mdns_local_name(self : Settings) -> String? {
self.mdns_local_name
}
///|
pub fn Settings::answering_dtls_role(self : Settings) -> @dtls.Role? {
self.answering_dtls_role
}
///|
pub fn Settings::verify_peer_fingerprint(self : Settings) -> Bool {
self.verify_peer_fingerprint
}
///|
pub fn Settings::dtls_certificate_key_type(
self : Settings,
) -> @dtls.CertificateKeyType {
self.dtls_certificate_key_type
}
///|
pub fn Settings::dtls_cipher_suites(
self : Settings,
) -> Array[@dtls.CipherSuite] {
self.dtls_cipher_suites.copy()
}
///|
pub fn Settings::dtls_psk(self : Settings) -> Bytes? {
self.dtls_psk
}
///|
pub fn Settings::dtls_psk_identity(self : Settings) -> Bytes? {
self.dtls_psk_identity
}
///|
pub fn Settings::dtls_srtp_profiles(
self : Settings,
) -> Array[@dtls.SrtpProtectionProfile] {
self.dtls_srtp_profiles.copy()
}
///|
pub fn Settings::dtls_flight_interval(self : Settings) -> Duration {
self.dtls_flight_interval
}
///|
pub fn Settings::dtls_mtu(self : Settings) -> Int {
self.dtls_mtu
}
///|
pub fn Settings::dtls_replay_window(self : Settings) -> Int {
self.dtls_replay_window
}
///|
pub fn Settings::srtp_replay_window(self : Settings) -> Int {
self.srtp_replay_window
}
///|
pub fn Settings::srtcp_replay_window(self : Settings) -> Int {
self.srtcp_replay_window
}
///|
pub fn Settings::sctp_max_payload_size(self : Settings) -> Int {
self.sctp_max_payload_size
}
///|
pub fn Settings::sctp_send_buffer_size(self : Settings) -> UInt64 {
self.sctp_send_buffer_size
}
///|
pub fn Settings::sctp_receive_buffer_size(self : Settings) -> UInt {
self.sctp_receive_buffer_size
}
///|
pub fn Settings::sctp_initial_rto(self : Settings) -> Duration {
self.sctp_initial_rto
}
///|
pub fn Settings::receive_mtu(self : Settings) -> Int {
self.receive_mtu
}
///|
pub fn Settings::ignore_rid_pause_for_recv(self : Settings) -> Bool {
self.ignore_rid_pause_for_recv
}
///|
pub fn Settings::write_ssrc_attributes_for_simulcast(self : Settings) -> Bool {
self.write_ssrc_attributes_for_simulcast
}
///|
pub struct Configuration {
ice_servers : Array[IceServer]
ice_transport_policy : @ice.IceTransportPolicy
mdns_mode : @mdns.Mode
host_candidates : Array[IceCandidate]
sctp_port : UInt16
max_message_size : UInt64
command_capacity : Int
event_capacity : Int
message_capacity : Int
settings : Settings
interceptor_pipeline : @interceptor.Pipeline
encoded_transform_pipeline : @media.EncodedTransformPipeline
}
///|
pub fn Configuration::new(
ice_servers? : Array[IceServer] = [],
ice_transport_policy? : @ice.IceTransportPolicy = All,
mdns_mode? : @mdns.Mode = QueryOnly,
host_candidates? : Array[IceCandidate] = [],
sctp_port? : UInt16 = 5000,
max_message_size? : UInt64 = 262144UL,
command_capacity? : Int = 64,
event_capacity? : Int = 256,
message_capacity? : Int = 1024,
settings? : Settings,
interceptor_pipeline? : @interceptor.Pipeline = @interceptor.Pipeline::new(),
encoded_transform_pipeline? : @media.EncodedTransformPipeline = @media.EncodedTransformPipeline::new(),
) -> Configuration raise RtcError {
let settings = settings.unwrap_or(Settings::new())
if command_capacity < 1 || event_capacity < 1 || message_capacity < 1 {
raise InvalidConfiguration(
"command, event, and message capacities must be positive",
)
}
if sctp_port == 0 || max_message_size == 0UL {
raise InvalidConfiguration(
"SCTP port and maximum message size must be nonzero",
)
}
for candidate in host_candidates {
if candidate.candidate_type() != Host || candidate.socket_address() is None {
raise InvalidConfiguration(
"host candidates must be resolved host transport candidates",
)
}
}
{
ice_servers,
ice_transport_policy,
mdns_mode,
host_candidates: host_candidates.copy(),
sctp_port,
max_message_size,
command_capacity,
event_capacity,
message_capacity,
settings,
interceptor_pipeline,
encoded_transform_pipeline,
}
}
///|
pub fn Configuration::command_capacity(self : Configuration) -> Int {
self.command_capacity
}
///|
pub fn Configuration::ice_servers(self : Configuration) -> Array[IceServer] {
self.ice_servers.copy()
}
///|
pub fn Configuration::ice_transport_policy(
self : Configuration,
) -> @ice.IceTransportPolicy {
self.ice_transport_policy
}
///|
pub fn Configuration::mdns_mode(self : Configuration) -> @mdns.Mode {
self.mdns_mode
}
///|
pub fn Configuration::event_capacity(self : Configuration) -> Int {
self.event_capacity
}
///|
pub fn Configuration::message_capacity(self : Configuration) -> Int {
self.message_capacity
}
///|
pub fn Configuration::settings(self : Configuration) -> Settings {
self.settings
}
///|
pub fn Configuration::host_candidates(
self : Configuration,
) -> Array[IceCandidate] {
self.host_candidates.copy()
}
///|
pub fn Configuration::sctp_port(self : Configuration) -> UInt16 {
self.sctp_port
}
///|
pub fn Configuration::max_message_size(self : Configuration) -> UInt64 {
self.max_message_size
}
///|
pub fn Configuration::interceptor_pipeline(
self : Configuration,
) -> @interceptor.Pipeline {
self.interceptor_pipeline
}
///|
pub fn Configuration::encoded_transform_pipeline(
self : Configuration,
) -> @media.EncodedTransformPipeline {
self.encoded_transform_pipeline
}
///|
pub(all) enum RtcMessage {
Rtp(track_id~ : String, packet~ : @rtp.Packet)
Rtcp(@rtcp.Packet)
DataChannel(channel_id~ : @sctp.StreamId, message~ : DataChannelMessage)
} derive(Debug, Eq)
///|
pub(all) enum PeerEvent {
SignalingStateChanged(SignalingState)
IceGatheringStateChanged(@ice.IceGatheringState)
IceConnectionStateChanged(@ice.IceConnectionState)
ConnectionStateChanged(PeerConnectionState)
IceCandidateDiscovered(IceCandidate)
TrackOpened(MediaTrack)
TrackClosed(String)
DataChannelOpened(DataChannel)
DataChannelClosed(@sctp.StreamId)
BufferedAmountLow(@sctp.StreamId)
NegotiationNeeded
Closed
} derive(Debug)
///|
pub(all) struct StatsReport {
timestamp : WallTime
bytes_sent : UInt64
bytes_received : UInt64
packets_sent : UInt64
packets_received : UInt64
packets_lost : UInt64
data_channels_opened : UInt
data_channels_closed : UInt
} derive(Debug, Eq)
///|
pub(all) struct CodecStats {
id : String
payload_type : Byte
mime_type : String
clock_rate : UInt
channels : UInt16
fmtp : String
} derive(Debug, Eq)
///|
pub(all) struct RtpStreamStats {
id : String
track_id : String
kind : MediaKind
outbound : Bool
ssrc : UInt
rid : String?
codec_id : String
packets : UInt64
bytes : UInt64
packets_lost : UInt64
jitter : UInt64
retransmitted_packets : UInt64
retransmitted_bytes : UInt64
active : Bool
} derive(Debug, Eq)
///|
pub(all) struct CandidatePairStats {
id : String
local_candidate : IceCandidate
remote_candidate : IceCandidate
priority : UInt64
state : @ice.CandidatePairState
nominated : Bool
selected : Bool
} derive(Debug, Eq)
///|
pub(all) struct DataChannelStats {
id : String
stream_id : @sctp.StreamId
label : String
protocol : String
state : DataChannelState
buffered_amount : UInt64
} derive(Debug, Eq)
///|
pub(all) struct TransportStats {
id : String
ice_state : @ice.IceConnectionState
dtls_state : @dtls.State?
selected_candidate_pair_id : String?
bytes_sent : UInt64
bytes_received : UInt64
packets_sent : UInt64
packets_received : UInt64
} derive(Debug, Eq)
///|
pub(all) struct DetailedStatsReport {
timestamp : WallTime
codecs : Array[CodecStats]
outbound_rtp : Array[RtpStreamStats]
inbound_rtp : Array[RtpStreamStats]
candidate_pairs : Array[CandidatePairStats]
data_channels : Array[DataChannelStats]
transport : TransportStats
} derive(Debug, Eq)