UNPKG

rtc-link

Version:

A library for managing WebRTC connections with multiplexed streams, anti-glare handling, and streamlined data channel setup.

67 lines (62 loc) 3.06 kB
import {asPlex, connectAndSend, listenAndConnectionAndRead$} from "rxprotoplex"; import {ReplaySubject, switchMap, take, tap, timeout, EMPTY} from "rxjs"; import {CHANNEL} from "./constants.js"; /** * Establishes a signaling connection between two streams, enabling WebRTC offer, answer, and ICE message * exchange via multiplexed channels. * * @param {Stream} stream1 - The first data stream to be connected. * @param {Stream} stream2 - The second data stream to be connected. * @param {Object} [config={}] - Optional configuration object. * @param {number} [config.iceCandidateTimeout=30000] - The timeout duration in milliseconds for ICE candidate messages. The timeout only starts after an offer or answer has been received. * * @description * The function uses the provided streams to create multiplexed (`plex`) connections that listen for * specific WebRTC signaling messages (offer, answer, and ICE candidates) on designated channels. * When a message is received on one channel, it is forwarded to the corresponding channel on the * target stream, effectively establishing a bi-directional signaling pipeline between the two streams. * * A close event from either stream triggers the closure of both signaling channels to avoid * unexpected behavior. * * - **Channels Used**: * - `CHANNEL.WEBRTC_OFFER` - For WebRTC offer messages. * - `CHANNEL.WEBRTC_ANSWER` - For WebRTC answer messages. * - `CHANNEL.WEBRTC_ICE` - For WebRTC ICE candidates. * * @example * establishSignalStream(stream1, stream2, { iceCandidateTimeout: 20000 }); * * @returns {Function} - A function that, when called, closes the signaling streams. */ const establishSignalStream = (stream1, stream2, config = {}) => { const { iceCandidateTimeout = 30000 } = config; const plex1 = asPlex(stream1); const plex2 = asPlex(stream2); const cleanups = []; [[plex1, plex2], [plex2, plex1]].forEach(([from, target]) => { const triggered = new ReplaySubject(1); cleanups.push( listenAndConnectionAndRead$(from, CHANNEL.WEBRTC_OFFER) .pipe(take(1), tap(() => triggered.next())) .subscribe(connectAndSend(target, CHANNEL.WEBRTC_OFFER)), listenAndConnectionAndRead$(from, CHANNEL.WEBRTC_ANSWER) .pipe(take(1), tap(() => triggered.next())) .subscribe(connectAndSend(target, CHANNEL.WEBRTC_ANSWER)), triggered.pipe( take(1), switchMap(() => listenAndConnectionAndRead$(from, CHANNEL.WEBRTC_ICE) .pipe( timeout({ each: iceCandidateTimeout, // Configurable timeout for ICE candidates with: () => EMPTY }) ) ) ).subscribe(connectAndSend(target, CHANNEL.WEBRTC_ICE)) ); }); return () => cleanups.forEach((obs) => obs.unsubscribe()); } export {establishSignalStream};