webrtcStore.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import { defineStore } from "pinia";
  2. import { MSG_TYPE, MESSAGE_TYPE_USER, MESSAGE_TYPE_GROUP } from "@/common/constant/msgType";
  3. import { useWebSocketStore } from "@/stores/modules/webSocketStore";
  4. export const useWebRTCStore = defineStore("webrtc", {
  5. state: () => ({
  6. // WebRTC 连接实例
  7. peerConnection: null,
  8. // ICE 候选信息
  9. iceCandidates: [],
  10. // 连接状态
  11. connectionState: "disconnected",
  12. // 媒体流
  13. localStream: null,
  14. remoteStream: null,
  15. // 配置项
  16. config: {
  17. iceServers: [
  18. { urls: "stun:stun.l.google.com:19302" },
  19. // 可以添加更多 STUN/TURN 服务器
  20. ],
  21. },
  22. }),
  23. actions: {
  24. // 初始化 WebRTC 连接
  25. initConnection(MESSAGE_TYPE) {
  26. this.cleanup();
  27. const wsStore = useWebSocketStore();
  28. try {
  29. this.peerConnection = new RTCPeerConnection();
  30. // 设置事件监听: 对等方收到ice信息后,通过调用 addIceCandidate 将接收的候选者信息传递给浏览器的ICE代理
  31. this.peerConnection.onicecandidate = (event) => {
  32. if (event.candidate) {
  33. if (MESSAGE_TYPE === MESSAGE_TYPE_USER) {
  34. let candidate = {
  35. type: "offer_ice",
  36. iceCandidate: event.candidate,
  37. };
  38. wsStore.sendMessage({
  39. contentType: MSG_TYPE.AUDIO_ONLINE,
  40. type: "webrtc",
  41. messageType: MESSAGE_TYPE,
  42. content: JSON.stringify(candidate),
  43. });
  44. }
  45. if (MESSAGE_TYPE === MESSAGE_TYPE_GROUP) {
  46. this.iceCandidates.push(event.candidate);
  47. }
  48. }
  49. };
  50. // 监听 ICE 状态变化
  51. this.peerConnection.onconnectionstatechange = () => {
  52. this.connectionState = this.peerConnection.connectionState;
  53. };
  54. // 当连接成功后,从里面获取语音视频流: 监听 ICE candidate:包含语音视频流
  55. this.peerConnection.ontrack = (event) => {
  56. // 添加远程媒体流
  57. if (!this.remoteStream) {
  58. this.remoteStream = new MediaStream();
  59. }
  60. // 添加远程媒体流
  61. event.streams[0].getTracks().forEach((track) => {
  62. this.remoteStream.addTrack(track);
  63. });
  64. };
  65. console.log("WebRTC 连接初始化成功");
  66. } catch (error) {
  67. console.error("初始化 WebRTC 连接失败:", error);
  68. this.cleanup();
  69. throw error;
  70. }
  71. },
  72. // 添加本地媒体流
  73. async addLocalStream(stream) {
  74. if (!this.peerConnection) {
  75. throw new Error("WebRTC 连接未初始化");
  76. }
  77. this.localStream = stream;
  78. stream.getTracks().forEach((track) => {
  79. this.peerConnection.addTrack(track, stream);
  80. });
  81. },
  82. // 创建 Offer
  83. async createOffer() {
  84. if (!this.peerConnection) {
  85. throw new Error("WebRTC 连接未初始化");
  86. }
  87. try {
  88. const offer = await this.peerConnection.createOffer();
  89. await this.peerConnection.setLocalDescription(offer);
  90. return offer;
  91. } catch (error) {
  92. console.error("创建 Offer 失败:", error);
  93. throw error;
  94. }
  95. },
  96. // 创建 Answer
  97. async createAnswer() {
  98. if (!this.peerConnection) {
  99. throw new Error("WebRTC 连接未初始化");
  100. }
  101. try {
  102. const answer = await this.peerConnection.createAnswer();
  103. await this.peerConnection.setLocalDescription(answer);
  104. return answer;
  105. } catch (error) {
  106. console.error("创建 Answer 失败:", error);
  107. throw error;
  108. }
  109. },
  110. // 设置远程 Description
  111. async setRemoteDescription(desc) {
  112. if (!this.peerConnection) {
  113. throw new Error("WebRTC 连接未初始化");
  114. }
  115. try {
  116. await this.peerConnection.setRemoteDescription(desc);
  117. } catch (error) {
  118. console.error("设置远程 Description 失败:", error);
  119. throw error;
  120. }
  121. },
  122. // 添加 ICE 候选
  123. async addIceCandidate(candidate) {
  124. if (!this.peerConnection) {
  125. throw new Error("WebRTC 连接未初始化");
  126. }
  127. try {
  128. await this.peerConnection.addIceCandidate(candidate);
  129. } catch (error) {
  130. console.error("添加 ICE 候选失败:", error);
  131. throw error;
  132. }
  133. },
  134. // 清理资源
  135. cleanup() {
  136. if (this.peerConnection) {
  137. this.peerConnection.close();
  138. this.peerConnection = null;
  139. }
  140. if (this.localStream) {
  141. this.localStream.getTracks().forEach((track) => track.stop());
  142. this.localStream = null;
  143. }
  144. this.iceCandidates = [];
  145. this.connectionState = "disconnected";
  146. },
  147. },
  148. });