Struct str0m::Rtc

source ·
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§

Creates a new instance with default settings.

To configure the instance, use RtcConfig.

use str0m::Rtc;

let rtc = Rtc::new();

Creates a config builder that configures an Rtc instance.

let rtc = Rtc::builder()
    .ice_lite(true)
    .build();

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());

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());

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);

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);

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);

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();

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();

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);

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:

  1. The polled timeout is reached.
  2. New network input.

See Rtc instance documentation for how this is expected to be used in a loop.

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();
        }
    }
}

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);
}

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.

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.

Trait Implementations§

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Should always be Self
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more