The ML Platform Behind Uber's AI
Michelangelo AI: 우버의 엔드‑투‑엔드 머신러닝 플랫폼
Rust로 구현한 Sans I/O WebRTC 구현체
A Sans I/O WebRTC implementation in Rust.
개발 도구Rust · ShellGitHub @algesten직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A Sans I/O WebRTC implementation in Rust.
This is a Sans I/O implementation meaning the Rtc instance itself is not doing any network
talking. Furthermore it has no internal threads or async tasks. All operations are happening from the
calls of the public API.
This is deliberately not a standard RTCPeerConnection API since that isn't a great fit for Rust.
See more details in below section.
We are discussing str0m things on Discord. Join us using this invitation link.
The chat example shows how to connect multiple browsers
together and act as an SFU (Selective Forwarding Unit). The example
multiplexes all traffic over one server UDP socket and uses two threads
(one for the web server, and one for the SFU loop).
For the browser to do WebRTC, all traffic must be under TLS. The
project ships with a self-signed certificate that is used for the
examples. The certificate is for hostname str0m.test since TLD .test
should never resolve to a real DNS name.
cargo run --example chat
The log should prompt you to connect a browser to https://10.0.0.103:3000 – this will most likely cause a security warning that you must get the browser to accept.
The http-post example roughly illustrates how to receive
media data from a browser client. The example is single threaded and
is a bit simpler than the chat. It is a good starting point to understand the API.
cargo run --example http-post
To see how str0m is used in a real project, check out BitWHIP – a CLI WebRTC Agent written in Rust.
For passive connections, i.e. where the media and initial OFFER is made by a remote peer, we need these steps to open the connection.
// Instantiate a new Rtc instance.
let mut rtc = Rtc::new(Instant::now());
// Add some ICE candidate such as a locally bound UDP port.
let addr = "1.2.3.4:5000".parse().unwrap();
let candidate = Candidate::host(addr, "udp").unwrap();
rtc.add_local_candidate(candidate);
// Accept an incoming offer from the remote peer
// and get the corresponding answer.
let offer = todo!();
let answer = rtc.sdp_api().accept_offer(offer).unwrap();
// Forward the answer to the remote peer.
// Go to _run loop_
Active connections means we are making the inital OFFER and waiting for a remote ANSWER to start the connection.
// Instantiate a new Rtc instance.
let mut rtc = Rtc::new(Instant::now());
// Add some ICE candidate such as a locally bound UDP port.
let addr = "1.2.3.4:5000".parse().unwrap();
let candidate = Candidate::host(addr, "udp").unwrap();
rtc.add_local_candidate(candidate);
// Create a `SdpApi`. The change lets us make multiple changes
// before sending the offer.
let mut change = rtc.sdp_api();
// Do some change. A valid OFFER needs at least one "m-line" (media).
let mid = change.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None);
// Get the offer.
let (offer, pending) = change.apply().unwrap();
// Forward the offer to the remote peer and await the answer.
// How to transfer this is outside the scope for this library.
let answer = todo!();
// Apply answer.
rtc.sdp_api().accept_answer(pending, answer).unwrap();
// Go to _run loop_
str0m's API has one strict contract that the run loop is built around:
Every mutation of an
Rtcinstance must be followed by a complete drain ofpoll_outputuntil it returnsOutput::Timeout, before the next mutation on the sameRtc.
A "mutation" is anything that takes &mut Rtc (directly or through a
handle obtained from it). The common ones are:
Rtc::handle_input — feeding a network packet or a timeoutWriter::write / Writer::request_keyframe — sending mediaChannel::write — sending data channel dataSdpApi::apply / DirectApi::* — negotiationRtc::add_local_candidate / Rtc::add_remote_candidateAlways: mutate → drain to Timeout → mutate → drain to Timeout → …
Doing two mutations back-to-back without draining in between, or
waiting on I/O while the engine still has output queued, leaves the
engine in an inconsistent state and produces wrong behavior. Mutations
issued from inside the drain loop (e.g. calling Writer::write in
response to an Output::Event) are fine — the drain loop naturally
continues calling poll_output afterward and so the invariant holds.
Driving an Rtc forward follows the same six-step shape, regardless
of sync or async:
Input to handle_input, or
call into a writer / channel / SDP API.Rtc::poll_output.Output::Transmit is sent on the socket,
Output::Event is dispatched to the application, Output::Timeout
records the next deadline.poll_output returns Output::Timeout. Only then
is the engine fully drained.여기까지가 README 앞부분입니다. 나머지는 원문에서 읽을 수 있습니다. README 전문 보기 ↗