|
@@ -19,32 +19,32 @@
|
|
|
let socket = io();
|
|
|
const peers = {};
|
|
|
|
|
|
- // 初始化WebSocket连接
|
|
|
- socket.on("connect", () => {
|
|
|
- socket.emit("join", {room: room_id});
|
|
|
- });
|
|
|
+ // 初始化WebSocket连接
|
|
|
+ socket.on("connect", () => {
|
|
|
+ socket.emit("join", { room: room_id });
|
|
|
+ });
|
|
|
|
|
|
- // 获取麦克风权限
|
|
|
- async function init() {
|
|
|
+ // 获取麦克风权限
|
|
|
+ async function init() {
|
|
|
try {
|
|
|
- localStream = await navigator.mediaDevices.getUserMedia({
|
|
|
- audio: true,
|
|
|
- });
|
|
|
- document.getElementById("status").innerHTML = "麦克风已就绪";
|
|
|
- document.getElementById("micButton").disabled = false;
|
|
|
+ localStream = await navigator.mediaDevices.getUserMedia({
|
|
|
+ audio: true,
|
|
|
+ });
|
|
|
+ document.getElementById("status").innerHTML = "麦克风已就绪";
|
|
|
+ document.getElementById("micButton").disabled = false;
|
|
|
} catch (err) {
|
|
|
- console.error("Error accessing microphone:", err);
|
|
|
- document.getElementById("status").innerHTML = "无法访问麦克风: " + err.message;
|
|
|
- document.getElementById("micButton").disabled = true;
|
|
|
+ console.error("Error accessing microphone:", err);
|
|
|
+ document.getElementById("status").innerHTML = "无法访问麦克风: " + err.message;
|
|
|
+ document.getElementById("micButton").disabled = true;
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // 切换麦克风状态
|
|
|
- async function toggleMic() {
|
|
|
+ // 切换麦克风状态
|
|
|
+ async function toggleMic() {
|
|
|
if (!localStream) {
|
|
|
- console.error("No localStream available");
|
|
|
- document.getElementById("status").innerHTML = "麦克风未就绪,请刷新页面重试";
|
|
|
- return;
|
|
|
+ console.error("No localStream available");
|
|
|
+ document.getElementById("status").innerHTML = "麦克风未就绪,请刷新页面重试";
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
isMute = !isMute;
|
|
@@ -52,64 +52,74 @@
|
|
|
button.textContent = isMute ? "🎤 点击说话" : "🔴 正在说话";
|
|
|
|
|
|
localStream
|
|
|
- .getAudioTracks()
|
|
|
- .forEach((track) => (track.enabled = !isMute));
|
|
|
+ .getAudioTracks()
|
|
|
+ .forEach((track) => (track.enabled = !isMute));
|
|
|
|
|
|
if (!isMute) {
|
|
|
+ createPeerConnections();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建与房间内其他用户的对等连接
|
|
|
+ async function createPeerConnections() {
|
|
|
+ for (let peerId in peers) {
|
|
|
+ if (peerId !== socket.id) {
|
|
|
const peer = new RTCPeerConnection();
|
|
|
- peers[socket.id] = peer;
|
|
|
+ peers[peerId] = peer;
|
|
|
|
|
|
localStream
|
|
|
- .getTracks()
|
|
|
- .forEach((track) => peer.addTrack(track, localStream));
|
|
|
+ .getTracks()
|
|
|
+ .forEach((track) => peer.addTrack(track, localStream));
|
|
|
|
|
|
peer.onicecandidate = (e) => {
|
|
|
- if (e.candidate) {
|
|
|
- socket.emit("candidate", {
|
|
|
- candidate: e.candidate,
|
|
|
- room: room_id,
|
|
|
- target: socket.id, // 添加目标标识
|
|
|
- });
|
|
|
- }
|
|
|
+ if (e.candidate) {
|
|
|
+ socket.emit("candidate", {
|
|
|
+ candidate: e.candidate,
|
|
|
+ room: room_id,
|
|
|
+ target: peerId,
|
|
|
+ });
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
try {
|
|
|
- const offer = await peer.createOffer();
|
|
|
- await peer.setLocalDescription(offer);
|
|
|
-
|
|
|
- socket.emit("offer", {
|
|
|
- sdp: offer,
|
|
|
- room: room_id, // 使用room字段
|
|
|
- });
|
|
|
+ const offer = await peer.createOffer();
|
|
|
+ await peer.setLocalDescription(offer);
|
|
|
+
|
|
|
+ socket.emit("offer", {
|
|
|
+ sdp: offer,
|
|
|
+ room: room_id,
|
|
|
+ target: peerId,
|
|
|
+ });
|
|
|
} catch (err) {
|
|
|
- console.error("Error creating or sending offer:", err);
|
|
|
- document.getElementById("status").innerHTML = "创建连接时出错,请刷新页面重试";
|
|
|
+ console.error("Error creating or sending offer:", err);
|
|
|
+ document.getElementById("status").innerHTML = "创建连接时出错,请刷新页面重试";
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // WebRTC信令处理
|
|
|
- socket.on("offer", async (data) => {
|
|
|
- // 添加发送者过滤
|
|
|
+ // WebRTC信令处理
|
|
|
+ socket.on("offer", async (data) => {
|
|
|
if (data.sender === socket.id) return;
|
|
|
|
|
|
const peer = new RTCPeerConnection();
|
|
|
peers[data.sender] = peer;
|
|
|
|
|
|
peer.onicecandidate = (e) => {
|
|
|
- if (e.candidate) {
|
|
|
- socket.emit("candidate", {
|
|
|
- candidate: e.candidate,
|
|
|
- target: data.sender,
|
|
|
- room: room_id,
|
|
|
- });
|
|
|
- }
|
|
|
+ if (e.candidate) {
|
|
|
+ socket.emit("candidate", {
|
|
|
+ candidate: e.candidate,
|
|
|
+ target: data.sender,
|
|
|
+ room: room_id,
|
|
|
+ });
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
peer.ontrack = (e) => {
|
|
|
- const audio = document.createElement("audio");
|
|
|
- audio.srcObject = e.streams[0];
|
|
|
- audio.play();
|
|
|
+ const audio = document.createElement("audio");
|
|
|
+ audio.srcObject = e.streams[0];
|
|
|
+ audio.autoplay = true;
|
|
|
+ document.body.appendChild(audio);
|
|
|
};
|
|
|
|
|
|
await peer.setRemoteDescription(data.sdp);
|
|
@@ -117,28 +127,43 @@
|
|
|
await peer.setLocalDescription(answer);
|
|
|
|
|
|
socket.emit("answer", {
|
|
|
- sdp: answer,
|
|
|
- target: data.sender, // 指定目标用户
|
|
|
- room: room_id,
|
|
|
+ sdp: answer,
|
|
|
+ target: data.sender,
|
|
|
+ room: room_id,
|
|
|
});
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- socket.on("answer", (data) => {
|
|
|
+ socket.on("answer", (data) => {
|
|
|
if (data.sender === socket.id) return;
|
|
|
const peer = peers[data.sender];
|
|
|
if (peer) {
|
|
|
- peer.setRemoteDescription(data.sdp);
|
|
|
+ peer.setRemoteDescription(data.sdp);
|
|
|
}
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- // 修改candidate处理逻辑
|
|
|
- socket.on("candidate", (data) => {
|
|
|
+ socket.on("candidate", (data) => {
|
|
|
if (data.sender === socket.id) return;
|
|
|
const peer = peers[data.sender];
|
|
|
- peer.addIceCandidate(new RTCIceCandidate(data.candidate));
|
|
|
- });
|
|
|
+ if (peer) {
|
|
|
+ peer.addIceCandidate(new RTCIceCandidate(data.candidate));
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ socket.on("user_joined", (userId) => {
|
|
|
+ peers[userId] = null;
|
|
|
+ if (!isMute) {
|
|
|
+ createPeerConnections();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ socket.on("user_left", (userId) => {
|
|
|
+ if (peers[userId]) {
|
|
|
+ peers[userId].close();
|
|
|
+ delete peers[userId];
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- init();
|
|
|
+ init();
|
|
|
</script>
|
|
|
</body>
|
|
|
</html>
|