pub struct Rtc { /* private fields */ }
Expand description
Instance that does WebRTC. Main struct of the entire library.
Usage
let mut rtc = Rtc::new();
loop {
let timeout = match rtc.poll_output().unwrap() {
Output::Timeout(v) => v,
Output::Transmit(t) => {
// TODO: Send data to remote peer.
continue; // poll again
}
Output::Event(e) => {
// TODO: Handle event.
continue; // poll again
}
};
// TODO: Wait for one of two events, reaching `timeout`
// or receiving network input. Both are encapsualted
// in the Input enum.
let input: Input = todo!();
rtc.handle_input(input).unwrap();
}
Implementations§
source§impl Rtc
impl Rtc
sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new instance with default settings.
To configure the instance, use RtcConfig
.
use str0m::Rtc;
let rtc = Rtc::new();
sourcepub fn builder() -> RtcConfig
pub fn builder() -> RtcConfig
Creates a config builder that configures an Rtc
instance.
let rtc = Rtc::builder()
.ice_lite(true)
.build();
sourcepub fn is_alive(&self) -> bool
pub fn is_alive(&self) -> bool
Tests if this instance is still working.
Certain events will straight away disconnect the Rtc
instance, such as
the DTLS fingerprint from the SDP not matching that of the TLS negotiation
(since that would potentially indicate a MITM attack!).
The instance can be manually disconnected using Rtc::disconnect()
.
let mut rtc = Rtc::new();
assert!(rtc.is_alive());
rtc.disconnect();
assert!(!rtc.is_alive());
sourcepub fn disconnect(&mut self)
pub fn disconnect(&mut self)
Force disconnects the instance making Rtc::is_alive()
return false
.
This makes Rtc::poll_output
and Rtc::handle_input
go inert and not
produce anymore network output or events.
let mut rtc = Rtc::new();
rtc.disconnect();
assert!(!rtc.is_alive());
sourcepub fn add_local_candidate(&mut self, c: Candidate)
pub fn add_local_candidate(&mut self, c: Candidate)
Add a local ICE candidate. Local candidates are socket addresses the Rtc
instance
use for communicating with the peer.
This library has no built-in discovery of local network addresses on the host or NATed addresses via a STUN server or TURN server. The user of the library is expected to add new local candidates as they are discovered.
In WebRTC lingo, the Rtc
instance is permanently in a mode of Trickle Ice. It’s
however advisable to add at least one local candidate before commencing SDP negotiation.
let mut rtc = Rtc::new();
let a = "127.0.0.1:5000".parse().unwrap();
let c = Candidate::host(a).unwrap();
rtc.add_local_candidate(c);
sourcepub fn add_remote_candidate(&mut self, c: Candidate)
pub fn add_remote_candidate(&mut self, c: Candidate)
Add a remote ICE candidate. Remote candidates are addresses of the peer.
Remote candidates are typically added via receiving a remote Offer
or Answer
.
However for the case of Trickle Ice, this is the way to add remote candidaes
that are “trickled” from the other side.
let mut rtc = Rtc::new();
let a = "1.2.3.4:5000".parse().unwrap();
let c = Candidate::host(a).unwrap();
rtc.add_remote_candidate(c);
sourcepub fn ice_connection_state(&self) -> IceConnectionState
pub fn ice_connection_state(&self) -> IceConnectionState
Checks current connection state. This state is also obtained via
Event::IceConnectionStateChange
.
More details on connection states can be found in the ICE RFC.
let mut rtc = Rtc::new();
assert_eq!(rtc.ice_connection_state(), IceConnectionState::New);
sourcepub fn create_change_set(&mut self) -> ChangeSet<'_>
pub fn create_change_set(&mut self) -> ChangeSet<'_>
Make changes to the Rtc session. This is the entry point for making an Offer
.
The resulting ChangeSet
encapsulates changes to the Rtc
session that will
require an SDP negotiation.
The ChangeSet
allows us to make multiple changes in one go. Calling
ChangeSet::apply()
doesn’t apply the changes, but produces the Offer
that is to be sent to the remote peer. Only when the the remote peer responds with
an Answer
can the changes be made to the session. The call to accept the answer
is PendingChanges::accept_answer()
.
How to send the Offer
to the remote peer is not up to this library. Could be websocket,
a data channel or some other method of communication. See examples for a combination
of using HTTP POST
and data channels.
let mut rtc = Rtc::new();
let mut changes = rtc.create_change_set();
let mid_audio = changes.add_media(MediaKind::Audio, Direction::SendOnly, None);
let mid_video = changes.add_media(MediaKind::Video, Direction::SendOnly, None);
let offer = changes.apply().unwrap();
let json = serde_json::to_vec(&offer).unwrap();
sourcepub fn accept_offer(&mut self, offer: Offer) -> Result<Answer, RtcError>
pub fn accept_offer(&mut self, offer: Offer) -> Result<Answer, RtcError>
Accept an Offer
from the remote peer. If this call returns successfully, the
changes will have been made to the session. The resulting Answer
should be
sent to the remote peer.
Note. Rtc::pending_changes()
from a previous non-completed ChangeSet
are
rolled back when calling this function.
The incoming SDP is validated in various ways which can cause this call to fail.
Example of such problems would be an SDP without any m-lines, missing a=fingerprint
or if a=group
doesn’t match the number of m-lines.
// obtain offer from remote peer.
let json_offer: &[u8] = todo!();
let offer: Offer = serde_json::from_slice(json_offer).unwrap();
let mut rtc = Rtc::new();
let answer = rtc.accept_offer(offer).unwrap();
// send json_answer to remote peer.
let json_answer = serde_json::to_vec(&answer).unwrap();
sourcepub fn pending_changes(&mut self) -> Option<PendingChanges<'_>>
pub fn pending_changes(&mut self) -> Option<PendingChanges<'_>>
Obtain pending changes from a previous Rtc::create_change_set()
call.
The PendingChanges
allows us to either accept a remote answer, or
rollback the changes.
When this function returns None
there are no pending changes. Changes are
automatically rolled back on Rtc::accept_offer()
let mut rtc = Rtc::new();
let mut changes = rtc.create_change_set();
let mid = changes.add_media(MediaKind::Audio, Direction::SendOnly, None);
let offer = changes.apply().unwrap();
// send offer to remote peer, receive answer back
let answer: Answer = todo!();
let pending = rtc.pending_changes().unwrap().accept_answer(answer);
sourcepub fn poll_output(&mut self) -> Result<Output, RtcError>
pub fn poll_output(&mut self) -> Result<Output, RtcError>
Poll the Rtc
instance for output. Output can be three things, something to Transmit
via a UDP socket (maybe via a TURN server). An Event, such as receiving media data,
or a Timeout.
The user of the library is expected to continuously call this function and deal with
the output until it encounters an Output::Timeout
at which point no further output
is produced (if polled again, it will result in just another timeout).
After exhausting the poll_output
, the function will only produce more output again
when one of two things happen:
- The polled timeout is reached.
- New network input.
See Rtc
instance documentation for how this is expected to be used in a loop.
sourcepub fn accepts(&self, input: &Input<'_>) -> bool
pub fn accepts(&self, input: &Input<'_>) -> bool
Check if this Rtc
instance accepts the given input. This is used for demultiplexing
several Rtc
instances over the same UDP server socket.
Input::Timeout
is always accepted. Input::Receive
is tested against the nominated
ICE candidate. If that doesn’t match and the incoming data is a STUN packet, the accept call
is delegated to the ICE agent which recognises the remote peer from a=ufrag
/a=password
credentials negotiated in the SDP.
In a server setup, the server would try to find an Rtc
instances using Rtc::accepts()
.
The first found instance would be given the input via Rtc::handle_input()
.
// A vec holding the managed rtc instances. One instance per remote peer.
let mut rtcs = vec![Rtc::new(), Rtc::new(), Rtc::new()];
// Configure instances with local ice candidates etc.
loop {
// TODO poll_timeout() and handle the output.
let input: Input = todo!(); // read network data from socket.
for rtc in &mut rtcs {
if rtc.accepts(&input) {
rtc.handle_input(input).unwrap();
}
}
}
sourcepub fn handle_input(&mut self, input: Input<'_>) -> Result<(), RtcError>
pub fn handle_input(&mut self, input: Input<'_>) -> Result<(), RtcError>
Provide input to this Rtc
instance. Input is either a Input::Timeout
for some
time that was previously obtained from Rtc::poll_output()
, or Input::Receive
for network data.
Both the timeout and the network data contains a std::time::Instant
which drives
time forward in the instance. For network data, the intention is to record the time
of receiving the network data as precise as possible. This time is used to calculate
things like jitter and bandwidth.
It’s always okay to call Rtc::handle_input()
with a timeout, also before the
time obtained via Rtc::poll_output()
.
let mut rtc = Rtc::new();
loop {
let timeout: Instant = todo!(); // rtc.poll_output() until we get a timeout.
let input: Input = todo!(); // wait for network data or timeout.
rtc.handle_input(input);
}
sourcepub fn media(&mut self, mid: Mid) -> Option<Media<'_>>
pub fn media(&mut self, mid: Mid) -> Option<Media<'_>>
Get a Media
instance for inspecting and manipulating media. Media has a 1-1
relationship with “m-line” from the SDP. The Media
instance is used for media
regardless of current direction.
Apart from inspecting information about the media, there are two fundamental
operations. One is Media::writer()
for writing outgoing media data, the other
is Media::request_keyframe()
to request a PLI/FIR keyframe for incoming media data.
All media rows are announced via the Event::MediaAdded
event. This function
will return None
for any Mid
until that event has fired. This
is also the case for the mid
that comes from ChangeSet::add_media()
.
Incoming media data is via the Event::MediaData
event.
let mut rtc = Rtc::new();
let mid: Mid = todo!(); // obtain Mid from Event::MediaAdded
let media = rtc.media(mid).unwrap();
// TODO write media or request keyframe.
sourcepub fn channel(&mut self, id: ChannelId) -> Option<Channel<'_>>
pub fn channel(&mut self, id: ChannelId) -> Option<Channel<'_>>
Obtain handle for writing to a data channel.
This is first available when a ChannelId
is advertised via Event::ChannelOpen
.
The function returns None
also for IDs from ChangeSet::add_channel()
.
Incoming channel data is via the Event::ChannelData
event.
let mut rtc = Rtc::new();
let cid: ChannelId = todo!(); // obtain Mid from Event::ChannelOpen
let media = rtc.channel(cid).unwrap();
// TODO write data channel data.