|
@@ -1,79 +1,95 @@
|
|
|
<!-- templates/room.html -->
|
|
|
<!DOCTYPE html>
|
|
|
<html>
|
|
|
- <head>
|
|
|
+<head>
|
|
|
<title>语音聊天室 - {{ room_id }}</title>
|
|
|
+ <!-- 中文-->
|
|
|
+ <meta charset="UTF-8"></meta>
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
|
|
- </head>
|
|
|
- <body>
|
|
|
- <h1>语音聊天室 {{ room_id }}</h1>
|
|
|
- <button id="micButton" onclick="toggleMic()">🎤 点击说话</button>
|
|
|
- <div id="status"></div>
|
|
|
-
|
|
|
- <script>
|
|
|
- const room_id = "{{ room_id }}";
|
|
|
- let isMute = true;
|
|
|
- let localStream;
|
|
|
- let socket = io();
|
|
|
- const peers = {};
|
|
|
-
|
|
|
- // 初始化WebSocket连接
|
|
|
- socket.on("connect", () => {
|
|
|
- socket.emit("join", { room: room_id });
|
|
|
- });
|
|
|
-
|
|
|
- // 获取麦克风权限
|
|
|
- async function init() {
|
|
|
+</head>
|
|
|
+<body>
|
|
|
+<h1>语音聊天室 {{ room_id }}</h1>
|
|
|
+<button id="micButton" onclick="toggleMic()">🎤 点击说话</button>
|
|
|
+<div id="status"></div>
|
|
|
+
|
|
|
+<script>
|
|
|
+ const room_id = "{{ room_id }}";
|
|
|
+ let isMute = true;
|
|
|
+ let localStream;
|
|
|
+ let socket = io();
|
|
|
+ const peers = {};
|
|
|
+
|
|
|
+ // 初始化WebSocket连接
|
|
|
+ socket.on("connect", () => {
|
|
|
+ socket.emit("join", {room: room_id});
|
|
|
+ });
|
|
|
+
|
|
|
+ // 获取麦克风权限
|
|
|
+ async function init() {
|
|
|
try {
|
|
|
- localStream = await navigator.mediaDevices.getUserMedia({
|
|
|
- audio: true,
|
|
|
- });
|
|
|
- document.getElementById("status").innerHTML = "麦克风已就绪";
|
|
|
+ localStream = await navigator.mediaDevices.getUserMedia({
|
|
|
+ audio: true,
|
|
|
+ });
|
|
|
+ document.getElementById("status").innerHTML = "麦克风已就绪";
|
|
|
+ document.getElementById("micButton").disabled = false;
|
|
|
} catch (err) {
|
|
|
- console.error("Error accessing microphone:", err);
|
|
|
+ console.error("Error accessing microphone:", err);
|
|
|
+ document.getElementById("status").innerHTML = "无法访问麦克风: " + err.message;
|
|
|
+ document.getElementById("micButton").disabled = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 切换麦克风状态
|
|
|
+ async function toggleMic() {
|
|
|
+ if (!localStream) {
|
|
|
+ console.error("No localStream available");
|
|
|
+ document.getElementById("status").innerHTML = "麦克风未就绪,请刷新页面重试";
|
|
|
+ return;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // 切换麦克风状态
|
|
|
- async function toggleMic() {
|
|
|
isMute = !isMute;
|
|
|
const button = document.getElementById("micButton");
|
|
|
button.textContent = isMute ? "🎤 点击说话" : "🔴 正在说话";
|
|
|
|
|
|
localStream
|
|
|
- .getAudioTracks()
|
|
|
- .forEach((track) => (track.enabled = !isMute));
|
|
|
+ .getAudioTracks()
|
|
|
+ .forEach((track) => (track.enabled = !isMute));
|
|
|
|
|
|
if (!isMute) {
|
|
|
- const peer = new RTCPeerConnection();
|
|
|
- peers[socket.id] = peer;
|
|
|
-
|
|
|
- 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, // 添加目标标识
|
|
|
- });
|
|
|
+ const peer = new RTCPeerConnection();
|
|
|
+ peers[socket.id] = peer;
|
|
|
+
|
|
|
+ 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, // 添加目标标识
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ const offer = await peer.createOffer();
|
|
|
+ await peer.setLocalDescription(offer);
|
|
|
+
|
|
|
+ socket.emit("offer", {
|
|
|
+ sdp: offer,
|
|
|
+ room: room_id, // 使用room字段
|
|
|
+ });
|
|
|
+ } catch (err) {
|
|
|
+ console.error("Error creating or sending offer:", err);
|
|
|
+ document.getElementById("status").innerHTML = "创建连接时出错,请刷新页面重试";
|
|
|
}
|
|
|
- };
|
|
|
-
|
|
|
- const offer = await peer.createOffer();
|
|
|
- await peer.setLocalDescription(offer);
|
|
|
-
|
|
|
- socket.emit("offer", {
|
|
|
- sdp: offer,
|
|
|
- room: room_id, // 使用room字段
|
|
|
- });
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // WebRTC信令处理
|
|
|
- socket.on("offer", async (data) => {
|
|
|
+ // WebRTC信令处理
|
|
|
+ socket.on("offer", async (data) => {
|
|
|
// 添加发送者过滤
|
|
|
if (data.sender === socket.id) return;
|
|
|
|
|
@@ -81,19 +97,19 @@
|
|
|
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.play();
|
|
|
};
|
|
|
|
|
|
await peer.setRemoteDescription(data.sdp);
|
|
@@ -101,28 +117,28 @@
|
|
|
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) => {
|
|
|
+ // 修改candidate处理逻辑
|
|
|
+ socket.on("candidate", (data) => {
|
|
|
if (data.sender === socket.id) return;
|
|
|
const peer = peers[data.sender];
|
|
|
peer.addIceCandidate(new RTCIceCandidate(data.candidate));
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- init();
|
|
|
- </script>
|
|
|
- </body>
|
|
|
+ init();
|
|
|
+</script>
|
|
|
+</body>
|
|
|
</html>
|