Last active
June 27, 2024 16:32
-
-
Save michaelneu/3c6c5f289140bdc23c8b2551a83d3f64 to your computer and use it in GitHub Desktop.
WebRTC video chat (see https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling)
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>a</title> | |
</head> | |
<body> | |
<video playsinline autoplay></video> | |
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> | |
<script> | |
(async () => { | |
const video = document.querySelector("video"); | |
const ws = new WebSocket("ws://localhost:3030/ws"); | |
ws.addEventListener("open", () => { | |
console.log("ws connected"); | |
}); | |
const send = (message) => ws.send(JSON.stringify(message)); | |
const peer = new RTCPeerConnection({ | |
iceServers: [ | |
{ | |
urls: "stun:stun.stunprotocol.org", | |
}, | |
], | |
}); | |
ws.addEventListener("message", async ({ data }) => { | |
const message = JSON.parse(data); | |
if (message.type === "answer") { | |
const sdp = new RTCSessionDescription(message.sdp); | |
await peer.setRemoteDescription(sdp); | |
} | |
if (message.type === "ice-candidate" && message.target === "a" && message.candidate) { | |
const candidate = new RTCIceCandidate(message.candidate); | |
peer.addIceCandidate(candidate); | |
} | |
}); | |
peer.addEventListener("negotiationneeded", async (event) => { | |
const offer = await peer.createOffer(); | |
await peer.setLocalDescription(offer); | |
send({ | |
type: "offer", | |
sdp: peer.localDescription, | |
}); | |
}); | |
peer.addEventListener("icecandidate", async (event) => { | |
send({ | |
type: "ice-candidate", | |
target: "b", | |
candidate: event.candidate, | |
}); | |
}); | |
peer.addEventListener("track", async (event) => { | |
console.log(event.streams); | |
video.srcObject = event.streams[0]; | |
}); | |
const media = await navigator.mediaDevices.getUserMedia({ video: true }); | |
for (const track of media.getTracks()) { | |
peer.addTrack(track, media); | |
} | |
})(); | |
</script> | |
</body> | |
</html> |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>b</title> | |
</head> | |
<body> | |
<video playsinline autoplay></video> | |
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> | |
<script> | |
(async () => { | |
const video = document.querySelector("video"); | |
const ws = new WebSocket("ws://localhost:3030/ws"); | |
ws.addEventListener("open", () => { | |
console.log("ws connected"); | |
}); | |
const send = (message) => ws.send(JSON.stringify(message)); | |
const peer = new RTCPeerConnection({ | |
iceServers: [ | |
{ | |
urls: "stun:stun.stunprotocol.org", | |
}, | |
], | |
}); | |
ws.addEventListener("message", async ({ data }) => { | |
const message = JSON.parse(data); | |
if (message.type === "offer") { | |
const sdp = new RTCSessionDescription(message.sdp); | |
await peer.setRemoteDescription(sdp); | |
try { | |
const media = await navigator.mediaDevices.getUserMedia({ video: true }); | |
for (const track of media.getTracks()) { | |
peer.addTrack(track, media); | |
} | |
} catch { | |
// | |
} | |
const answer = await peer.createAnswer(); | |
await peer.setLocalDescription(answer); | |
send({ | |
type: "answer", | |
sdp: peer.localDescription, | |
}); | |
} | |
if (message.type === "ice-candidate" && message.target === "b" && message.candidate) { | |
const candidate = new RTCIceCandidate(message.candidate); | |
peer.addIceCandidate(candidate); | |
} | |
}); | |
peer.addEventListener("icecandidate", async (event) => { | |
send({ | |
type: "ice-candidate", | |
target: "a", | |
candidate: event.candidate, | |
}); | |
}); | |
peer.addEventListener("track", async (event) => { | |
console.log(event.streams); | |
video.srcObject = event.streams[0]; | |
}); | |
})(); | |
</script> | |
</body> | |
</html> |
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
{ | |
"name": "tmp", | |
"version": "1.0.0", | |
"main": "index.js", | |
"license": "MIT", | |
"scripts": { | |
"start": "ts-node index.ts" | |
}, | |
"dependencies": { | |
"@types/cors": "^2.8.5", | |
"@types/express": "^4.16.1", | |
"@types/express-ws": "^3.0.0", | |
"cors": "^2.8.5", | |
"express": "^4.17.1", | |
"express-ws": "^4.0.0", | |
"ts-node": "^8.2.0", | |
"typescript": "^3.5.1" | |
} | |
} |
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
Show hidden characters
{ | |
"compilerOptions": { | |
"target": "es5" | |
} | |
} |
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
import * as express from "express"; | |
import * as expressWs from "express-ws"; | |
import * as cors from "cors"; | |
import { createServer } from "http"; | |
const app = express(); | |
app.use(cors()); | |
const server = createServer(app); | |
const ws = expressWs(app, server).app; | |
const sockets = []; | |
ws.ws("/ws", (socket) => { | |
sockets.push(socket); | |
socket.addEventListener("close", () => { | |
const index = sockets.indexOf(socket); | |
sockets.splice(index, 1); | |
}); | |
socket.addEventListener("message", (event) => { | |
console.log(`<= ${event.data}`); | |
for (const s of sockets) { | |
s.send(event.data); | |
} | |
console.log(); | |
}) | |
}) | |
server.listen(3030, () => { | |
console.log("listening"); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment