room.html 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <!-- templates/room.html -->
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <title>语音聊天室 - {{ room_id }}</title>
  6. <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
  7. </head>
  8. <body>
  9. <h1>语音聊天室 {{ room_id }}</h1>
  10. <button id="micButton" onclick="toggleMic()">🎤 点击说话</button>
  11. <div id="status"></div>
  12. <script>
  13. const room_id = "{{ room_id }}";
  14. let isMute = true;
  15. let localStream;
  16. let socket = io();
  17. const peers = {};
  18. // 初始化WebSocket连接
  19. socket.on("connect", () => {
  20. socket.emit("join", { room: room_id });
  21. });
  22. // 获取麦克风权限
  23. async function init() {
  24. try {
  25. localStream = await navigator.mediaDevices.getUserMedia({
  26. audio: true,
  27. });
  28. document.getElementById("status").innerHTML = "麦克风已就绪";
  29. } catch (err) {
  30. console.error("Error accessing microphone:", err);
  31. }
  32. }
  33. // 切换麦克风状态
  34. async function toggleMic() {
  35. isMute = !isMute;
  36. const button = document.getElementById("micButton");
  37. button.textContent = isMute ? "🎤 点击说话" : "🔴 正在说话";
  38. localStream
  39. .getAudioTracks()
  40. .forEach((track) => (track.enabled = !isMute));
  41. if (!isMute) {
  42. const peer = new RTCPeerConnection();
  43. peers[socket.id] = peer;
  44. localStream
  45. .getTracks()
  46. .forEach((track) => peer.addTrack(track, localStream));
  47. peer.onicecandidate = (e) => {
  48. if (e.candidate) {
  49. socket.emit("candidate", {
  50. candidate: e.candidate,
  51. room: room_id,
  52. target: socket.id, // 添加目标标识
  53. });
  54. }
  55. };
  56. const offer = await peer.createOffer();
  57. await peer.setLocalDescription(offer);
  58. socket.emit("offer", {
  59. sdp: offer,
  60. room: room_id, // 使用room字段
  61. });
  62. }
  63. }
  64. // WebRTC信令处理
  65. socket.on("offer", async (data) => {
  66. // 添加发送者过滤
  67. if (data.sender === socket.id) return;
  68. const peer = new RTCPeerConnection();
  69. peers[data.sender] = peer;
  70. peer.onicecandidate = (e) => {
  71. if (e.candidate) {
  72. socket.emit("candidate", {
  73. candidate: e.candidate,
  74. target: data.sender,
  75. room: room_id,
  76. });
  77. }
  78. };
  79. peer.ontrack = (e) => {
  80. const audio = document.createElement("audio");
  81. audio.srcObject = e.streams[0];
  82. audio.play();
  83. };
  84. await peer.setRemoteDescription(data.sdp);
  85. const answer = await peer.createAnswer();
  86. await peer.setLocalDescription(answer);
  87. socket.emit("answer", {
  88. sdp: answer,
  89. target: data.sender, // 指定目标用户
  90. room: room_id,
  91. });
  92. });
  93. socket.on("answer", (data) => {
  94. if (data.sender === socket.id) return;
  95. const peer = peers[data.sender];
  96. if (peer) {
  97. peer.setRemoteDescription(data.sdp);
  98. }
  99. });
  100. // 修改candidate处理逻辑
  101. socket.on("candidate", (data) => {
  102. if (data.sender === socket.id) return;
  103. const peer = peers[data.sender];
  104. peer.addIceCandidate(new RTCIceCandidate(data.candidate));
  105. });
  106. init();
  107. </script>
  108. </body>
  109. </html>