Created
July 12, 2023 06:45
-
-
Save guiseek/ca6d240f53404d135139861fdec35149 to your computer and use it in GitHub Desktop.
WebRTC Local
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function create<K extends keyof HTMLElementTagNameMap>( | |
name: K, | |
attributes: Partial<HTMLElementTagNameMap[K]>, | |
...children: Element[] | |
): HTMLElementTagNameMap[K] { | |
const el = document.createElement(name) | |
if (children) el.append(...children) | |
return Object.assign(el, attributes) | |
} | |
interface Message { | |
id: string | |
type: 'offer' | 'answer' | 'candidate' | |
sdp?: RTCSessionDescriptionInit | |
candidate?: RTCIceCandidateInit | |
} | |
interface CallbackFunction<T> { | |
(value: T): void | |
} | |
class Signaling { | |
id = crypto.randomUUID() | |
#onOffer: CallbackFunction<Message>[] = [] | |
set onOffer(cb: CallbackFunction<Message>) { | |
this.#onOffer.push(cb) | |
} | |
#onAnswer: CallbackFunction<Message>[] = [] | |
set onAnswer(cb: CallbackFunction<Message>) { | |
this.#onAnswer.push(cb) | |
} | |
#onCandidate: CallbackFunction<Message>[] = [] | |
set onCandidate(cb: CallbackFunction<Message>) { | |
this.#onCandidate.push(cb) | |
} | |
#provider | |
constructor(name: string) { | |
this.#provider = new BroadcastChannel(name) | |
this.#provider.onmessage = async ({data}: MessageEvent<Message>) => { | |
if (data.type === 'answer') for (const cb of this.#onAnswer) cb(data) | |
if (data.type === 'offer') for (const cb of this.#onOffer) cb(data) | |
if (data.type === 'candidate') | |
for (const cb of this.#onCandidate) cb(data) | |
} | |
} | |
sendOffer(sdp: RTCSessionDescription) { | |
this.#provider.postMessage({id: this.id, type: 'offer', sdp: sdp.toJSON()}) | |
} | |
sendAnswer(sdp: RTCSessionDescription) { | |
this.#provider.postMessage({id: this.id, type: 'answer', sdp: sdp.toJSON()}) | |
} | |
sendCandidate({candidate}: RTCIceCandidateInit) { | |
this.#provider.postMessage({type: 'candidate', id: this.id, candidate}) | |
} | |
} | |
const signaling = new Signaling('webrtc') | |
const peer = new RTCPeerConnection() | |
peer.onicecandidate = ({candidate}) => { | |
if (candidate) signaling.sendCandidate(new RTCIceCandidate(candidate)) | |
} | |
signaling.onAnswer = (answer) => { | |
if (answer.id !== signaling.id && answer.sdp) { | |
console.log(answer) | |
if (peer.signalingState !== 'stable') { | |
peer.setRemoteDescription(answer.sdp) | |
} | |
} | |
} | |
signaling.onOffer = async (offer) => { | |
if (offer.id !== signaling.id && offer.sdp) { | |
if (peer.signalingState !== 'stable') { | |
peer.setRemoteDescription(offer.sdp) | |
peer.createAnswer().then(async (sdp) => { | |
await peer.setLocalDescription(sdp) | |
signaling.sendAnswer(new RTCSessionDescription(sdp)) | |
}) | |
} | |
} | |
} | |
const channel = peer.createDataChannel('channel') | |
channel.onmessage = ({data}) => { | |
console.log(`%c ${data}`, 'font-size: 24px') | |
} | |
channel.onopen = console.log | |
peer.oniceconnectionstatechange = console.log | |
peer.onconnectionstatechange = console.log | |
peer.onnegotiationneeded = (ev) => { | |
console.log(ev) | |
if (peer.signalingState !== 'have-remote-offer') { | |
peer.createOffer().then(async (sdp) => { | |
await peer.setLocalDescription(sdp) | |
signaling.sendOffer(new RTCSessionDescription(sdp)) | |
}) | |
} | |
} | |
navigator.mediaDevices.getUserMedia({audio: { | |
echoCancellation: true | |
}}).then((stream) => { | |
const local = create('audio', { | |
srcObject: stream, | |
controls: true, | |
autoplay: true, | |
muted: true, | |
}) | |
document.body.appendChild(local) | |
stream.getTracks().forEach((track) => { | |
peer.addTrack(track, stream) | |
}) | |
}) | |
peer.ontrack = ({track, streams}) => { | |
console.log(track, streams) | |
const remote = create('audio', { | |
srcObject: streams[0], | |
controls: true, | |
autoplay: true, | |
}) | |
document.body.appendChild(remote) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment