Skip to content

Instantly share code, notes, and snippets.

@x8BitRain
Last active March 17, 2024 12:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save x8BitRain/803fcff8642c74dc9f95a4ae39284e2d to your computer and use it in GitHub Desktop.
Save x8BitRain/803fcff8642c74dc9f95a4ae39284e2d to your computer and use it in GitHub Desktop.
Simple WebRTC chat class
const rtcConfig = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }
const createOfferFromSdp = (sdp: string): RTCSessionDescriptionInit => {
return {
type: 'offer',
sdp,
}
}
const createAnswerFromSdp = (sdp: string): RTCSessionDescriptionInit => {
return {
type: 'answer',
sdp,
}
}
// The flow of creating a connection is follows the form of a handshake, Follow the numbered execution steps 1, 2 and 3 (Bob and Alice being separate clients).
// 1. Bob creates the connection request, sends that signal (`this.peerConnection.localDescription.sdp` from his client) to Alice (typically via websocket).
// 2. Alice recieves the connection request, and joins it by sending back an accept signal (`this.peerConnection.localDescription.sdp` from her client)
// 3. Bob receives Alice's accept signal on his client and completes the connection by applying `(answerSdp: string)` to his `this.peerConnection`
// 4. Bob and or Alice can now send each other messages with their respective `this.dataChannel` instances.
class RTCService {
peerConnection: RTCPeerConnection
dataChannel: RTCDataChannel | undefined
constructor() {
this.peerConnection = new RTCPeerConnection(rtcConfig)
}
// 1. Bob
async startConnection(): Promise<string> {
this.dataChannel = this.peerConnection.createDataChannel('chat')
await this.peerConnection.setLocalDescription(
await this.peerConnection.createOffer(),
)
// This runs when after connection achieved
this.peerConnection.addEventListener(
'connectionstatechange',
(event: Event) => {
console.log(event, 'connectionstatechange')
if (
this.peerConnection.connectionState === 'connected' &&
this.dataChannel
) {
// Connected!
console.log('connected!')
}
},
)
this.dataChannel.addEventListener('open', () => {
console.log('data channel open!')
})
// This runs when receiving a message
this.dataChannel.addEventListener('message', (e) => {
// You have the message!
console.log(e.data)
})
return new Promise((resolve) => {
this.peerConnection.addEventListener('icecandidate', ({ candidate }) => {
if (candidate == null && this.peerConnection.localDescription) {
// OFFER from Bob. Send this to alice.
console.log(JSON.stringify(this.peerConnection.localDescription.sdp))
resolve(this.peerConnection.localDescription.sdp)
}
})
})
}
// 3. Bob
async acceptConnectionRequest(answerSdp: string) {
const answerObject = createAnswerFromSdp(answerSdp)
const answerDesc = new RTCSessionDescription(answerObject)
await this.peerConnection.setRemoteDescription(answerDesc)
}
// 2. Alice
async joinConnection(offerSdp: string): Promise<string> {
const acceptObject = createOfferFromSdp(offerSdp)
const offerDesc = new RTCSessionDescription(acceptObject)
await this.peerConnection.setRemoteDescription(offerDesc)
await this.peerConnection.setLocalDescription(
await this.peerConnection.createAnswer(),
)
this.peerConnection.addEventListener('datachannel', ({ channel }) => {
// CONNECTED!
this.dataChannel = channel
// BOB sent a message to ALICE
this.dataChannel.addEventListener('message', (event: MessageEvent) => {
// You have the message!
console.log(event.data)
})
})
return new Promise((resolve) => {
this.peerConnection.addEventListener('icecandidate', (e) => {
if (e.candidate == null && this.peerConnection.localDescription) {
// ALICE has her answer. Send this to bob.
console.log(this.peerConnection.localDescription.sdp)
resolve(this.peerConnection.localDescription.sdp)
}
})
})
}
// 4. Bob or Alice
sendMessage(message: string) {
this.dataChannel?.send(message)
}
}
export default new RTCService()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment