@libp2p/webrtc-peer
Version:
Simple one-to-one WebRTC data channels
82 lines • 3.23 kB
JavaScript
import { WebRTCPeer } from './peer.js';
import { WebRTCHandshake } from './handshake.js';
import randombytes from 'iso-random-stream/src/random.js';
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
import { pEvent } from 'p-event';
import delay from 'delay';
import { CustomEvent } from '@libp2p/interfaces/events';
import { logger } from '@libp2p/logger';
const log = logger('libp2p:webrtc-peer:initator');
const ICECOMPLETE_TIMEOUT = 1000;
export class WebRTCInitiator extends WebRTCPeer {
constructor(opts = {}) {
super({
...opts,
logPrefix: 'initiator'
});
this.handleDataChannelEvent({
channel: this.peerConnection.createDataChannel(opts.dataChannelLabel ?? uint8ArrayToString(randombytes(20), 'hex').slice(0, 7), opts.dataChannelInit)
});
this.handshake = new WebRTCInitiatorHandshake({
log: this.log,
peerConnection: this.peerConnection,
wrtc: this.wrtc,
offerOptions: opts.offerOptions
});
this.handshake.addEventListener('signal', event => {
this.dispatchEvent(new CustomEvent('signal', { detail: event.detail }));
});
}
handleSignal(signal) {
this.handshake.handleSignal(signal).catch(err => {
this.log('error handling signal %o %o', signal, err);
});
}
}
class WebRTCInitiatorHandshake extends WebRTCHandshake {
constructor(options) {
super(options);
this.options = options;
this.status = 'idle';
this.peerConnection.addEventListener('icecandidate', (event) => {
if (event.candidate == null) {
return;
}
const signal = {
type: 'candidate',
candidate: {
candidate: event.candidate.candidate,
sdpMLineIndex: event.candidate.sdpMLineIndex,
sdpMid: event.candidate.sdpMid
}
};
log.trace('create candidate', signal);
this.dispatchEvent(new CustomEvent('signal', {
detail: signal
}));
this.dispatchEvent(new CustomEvent('ice-candidate'));
});
}
async handleRenegotiate() {
if (this.status === 'negotiating') {
this.log('already negotiating, queueing');
return;
}
this.status = 'negotiating';
const offer = await this.peerConnection.createOffer(this.options.offerOptions);
await this.peerConnection.setLocalDescription(offer);
// wait for at least one candidate before sending the offer
await pEvent(this, 'ice-candidate');
await delay(ICECOMPLETE_TIMEOUT);
log.trace('renegotiate', this.peerConnection.localDescription);
this.dispatchEvent(new CustomEvent('signal', {
detail: this.peerConnection.localDescription ?? offer
}));
}
async handleAnswer(signal) {
log.trace('handle answer', signal);
await this.peerConnection.setRemoteDescription(new this.wrtc.RTCSessionDescription(signal));
this.status = 'idle';
}
}
//# sourceMappingURL=initiator.js.map