154 lines
3.8 KiB
JavaScript
154 lines
3.8 KiB
JavaScript
import { useEffect, useState, useRef } from "react";
|
|
import Peer from "peerjs";
|
|
|
|
const getUserMedia =
|
|
navigator.getUserMedia ||
|
|
navigator.webkitGetUserMedia ||
|
|
navigator.mozGetUserMedia;
|
|
|
|
function useSession(onConnectionOpen, onConnectionSync) {
|
|
const [peerId, setPeerId] = useState(null);
|
|
const [peer, setPeer] = useState(null);
|
|
const [connections, setConnections] = useState({});
|
|
const [streams, setStreams] = useState({});
|
|
// Keep a ref to the streams in order to clean them up on unmount
|
|
const streamsRef = useRef(streams);
|
|
|
|
function addConnection(connection) {
|
|
setConnections(prevConnnections => ({
|
|
...prevConnnections,
|
|
[connection.peer]: connection
|
|
}));
|
|
}
|
|
|
|
function addStream(stream, id) {
|
|
setStreams(prevStreams => ({
|
|
...prevStreams,
|
|
[id]: stream
|
|
}));
|
|
}
|
|
|
|
useEffect(() => {
|
|
setPeer(new Peer());
|
|
|
|
return () => {
|
|
for (let stream of Object.values(streamsRef.current)) {
|
|
for (let track of stream.getTracks()) {
|
|
track.stop();
|
|
}
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
// Update stream refs
|
|
useEffect(() => {
|
|
streamsRef.current = streams;
|
|
}, [streams, streamsRef]);
|
|
|
|
useEffect(() => {
|
|
function handleOpen(id) {
|
|
setPeerId(id);
|
|
|
|
getUserMedia({ video: true, audio: true }, stream => {
|
|
addStream(stream, id);
|
|
});
|
|
}
|
|
|
|
function handleConnection(connection) {
|
|
connection.on("open", () => {
|
|
const metadata = connection.metadata;
|
|
if (metadata.sync) {
|
|
connection.send({
|
|
id: "sync",
|
|
data: Object.keys(connections)
|
|
});
|
|
if (onConnectionSync) {
|
|
onConnectionSync(connection);
|
|
}
|
|
}
|
|
|
|
addConnection(connection, false);
|
|
|
|
if (onConnectionOpen) {
|
|
onConnectionOpen(connection);
|
|
}
|
|
});
|
|
|
|
function removeConnection() {
|
|
setConnections(prevConnections => {
|
|
const { [connection.peer]: old, ...rest } = prevConnections;
|
|
return rest;
|
|
});
|
|
}
|
|
connection.on("close", removeConnection);
|
|
connection.on("error", removeConnection);
|
|
}
|
|
|
|
function handleCall(call) {
|
|
call.answer(streams[peerId]);
|
|
call.on("stream", remoteStream => {
|
|
addStream(remoteStream, call.peer);
|
|
});
|
|
}
|
|
|
|
if (!peer) {
|
|
return;
|
|
}
|
|
|
|
peer.on("open", handleOpen);
|
|
peer.on("connection", handleConnection);
|
|
peer.on("call", handleCall);
|
|
return () => {
|
|
peer.removeListener("open", handleOpen);
|
|
peer.removeListener("connection", handleConnection);
|
|
peer.removeListener("call", handleCall);
|
|
};
|
|
}, [peer, peerId, connections, onConnectionOpen, onConnectionSync, streams]);
|
|
|
|
function sync(connectionIds) {
|
|
for (let connectionId of connectionIds) {
|
|
if (connectionId in connections) {
|
|
continue;
|
|
}
|
|
const connection = peer.connect(connectionId, {
|
|
metadata: { sync: false }
|
|
});
|
|
addConnection(connection, false);
|
|
const call = peer.call(connectionId, streams[peerId]);
|
|
call.on("stream", stream => {
|
|
addStream(stream, connectionId);
|
|
});
|
|
}
|
|
}
|
|
|
|
function connectTo(connectionId) {
|
|
if (connectionId in connections) {
|
|
return;
|
|
}
|
|
const connection = peer.connect(connectionId, {
|
|
metadata: { sync: true }
|
|
});
|
|
connection.on("open", () => {
|
|
connection.on("data", data => {
|
|
if (data.id === "sync") {
|
|
sync(data.data);
|
|
}
|
|
});
|
|
if (onConnectionOpen) {
|
|
onConnectionOpen(connection);
|
|
}
|
|
});
|
|
|
|
const call = peer.call(connectionId, streams[peerId]);
|
|
call.on("stream", remoteStream => {
|
|
addStream(remoteStream, connectionId);
|
|
});
|
|
|
|
addConnection(connection, false);
|
|
}
|
|
|
|
return { peer, peerId, streams, connections, connectTo };
|
|
}
|
|
|
|
export default useSession;
|