Browse Source

Merge branch 'master' of https://git.nanodreamtech.com/wkw/wallet_app

wkw 1 tuần trước cách đây
mục cha
commit
cd1b62fcb4
2 tập tin đã thay đổi với 184 bổ sung1 xóa
  1. 168 0
      src/stores/modules/webrtcStore.js
  2. 16 1
      src/views/im/chat/index.vue

+ 168 - 0
src/stores/modules/webrtcStore.js

@@ -0,0 +1,168 @@
+import { defineStore } from "pinia";
+import { MSG_TYPE, MESSAGE_TYPE_USER } from "@/common/constant/msgType";
+import { useWebSocketStore } from "@/stores/modules/webSocketStore";
+
+export const useWebRTCStore = defineStore("webrtc", {
+  state: () => ({
+    // WebRTC 连接实例
+    peerConnection: null,
+
+    // ICE 候选信息
+    iceCandidates: [],
+
+    // 连接状态
+    connectionState: "disconnected",
+
+    // 媒体流
+    localStream: null,
+    remoteStream: null,
+
+    // 配置项
+    config: {
+      iceServers: [
+        { urls: "stun:stun.l.google.com:19302" },
+        // 可以添加更多 STUN/TURN 服务器
+      ],
+    },
+  }),
+  webSocketStore: useWebSocketStore(),
+  actions: {
+    // 初始化 WebRTC 连接
+    initConnection(MESSAGE_TYPE, data) {
+      this.cleanup();
+
+      try {
+        this.peerConnection = new RTCPeerConnection();
+
+        // 设置事件监听: 对等方收到ice信息后,通过调用 addIceCandidate 将接收的候选者信息传递给浏览器的ICE代理
+        this.peerConnection.onicecandidate = (event) => {
+          if (event.candidate) {
+            if (MESSAGE_TYPE === MESSAGE_TYPE_USER) {
+              let candidate = {
+                type: "offer_ice",
+                iceCandidate: event.candidate,
+              };
+              this.$webSocketStore.sendMessage({
+                contentType: MSG_TYPE.AUDIO_ONLINE,
+                type: "webrtc",
+                messageType: MESSAGE_TYPE,
+                content: JSON.stringify(candidate),
+                ...data,
+              });
+            }
+            if (MESSAGE_TYPE === MESSAGE_TYPE_GROUP) {
+              this.iceCandidates.push(event.candidate);
+            }
+          }
+        };
+
+        this.peerConnection.onconnectionstatechange = () => {
+          this.connectionState = this.peerConnection.connectionState;
+        };
+
+        this.peerConnection.ontrack = (event) => {
+          if (!this.remoteStream) {
+            this.remoteStream = new MediaStream();
+          }
+          event.streams[0].getTracks().forEach((track) => {
+            this.remoteStream.addTrack(track);
+          });
+        };
+
+        console.log("WebRTC 连接初始化成功");
+      } catch (error) {
+        console.error("初始化 WebRTC 连接失败:", error);
+        this.cleanup();
+        throw error;
+      }
+    },
+
+    // 添加本地媒体流
+    async addLocalStream(stream) {
+      if (!this.peerConnection) {
+        throw new Error("WebRTC 连接未初始化");
+      }
+
+      this.localStream = stream;
+      stream.getTracks().forEach((track) => {
+        this.peerConnection.addTrack(track, stream);
+      });
+    },
+
+    // 创建 Offer
+    async createOffer() {
+      if (!this.peerConnection) {
+        throw new Error("WebRTC 连接未初始化");
+      }
+
+      try {
+        const offer = await this.peerConnection.createOffer();
+        await this.peerConnection.setLocalDescription(offer);
+        return offer;
+      } catch (error) {
+        console.error("创建 Offer 失败:", error);
+        throw error;
+      }
+    },
+
+    // 创建 Answer
+    async createAnswer() {
+      if (!this.peerConnection) {
+        throw new Error("WebRTC 连接未初始化");
+      }
+
+      try {
+        const answer = await this.peerConnection.createAnswer();
+        await this.peerConnection.setLocalDescription(answer);
+        return answer;
+      } catch (error) {
+        console.error("创建 Answer 失败:", error);
+        throw error;
+      }
+    },
+
+    // 设置远程 Description
+    async setRemoteDescription(desc) {
+      if (!this.peerConnection) {
+        throw new Error("WebRTC 连接未初始化");
+      }
+
+      try {
+        await this.peerConnection.setRemoteDescription(desc);
+      } catch (error) {
+        console.error("设置远程 Description 失败:", error);
+        throw error;
+      }
+    },
+
+    // 添加 ICE 候选
+    async addIceCandidate(candidate) {
+      if (!this.peerConnection) {
+        throw new Error("WebRTC 连接未初始化");
+      }
+
+      try {
+        await this.peerConnection.addIceCandidate(candidate);
+      } catch (error) {
+        console.error("添加 ICE 候选失败:", error);
+        throw error;
+      }
+    },
+
+    // 清理资源
+    cleanup() {
+      if (this.peerConnection) {
+        this.peerConnection.close();
+        this.peerConnection = null;
+      }
+
+      if (this.localStream) {
+        this.localStream.getTracks().forEach((track) => track.stop());
+        this.localStream = null;
+      }
+
+      this.iceCandidates = [];
+      this.connectionState = "disconnected";
+    },
+  },
+});

+ 16 - 1
src/views/im/chat/index.vue

@@ -149,7 +149,7 @@
           <svg-icon class="tool-icon" name="ps" />
           <div>拍摄</div>
         </div>
-        <div class="tool-btn">
+        <div class="tool-btn" @click="startAudioOnline">
           <svg-icon class="tool-icon" name="yyth" />
           <div>语音通话</div>
         </div>
@@ -176,6 +176,8 @@ import { Capacitor } from "@capacitor/core";
 import { MSG_TYPE, MESSAGE_TYPE_USER } from "@/common/constant/msgType";
 import  messageAudio from "@/views/im/components/messageAudio/index.vue";
 import { showToast } from 'vant';
+import {useWebRTCStore} from "@/stores/modules/webrtcStore";
+
 
 const IM_PATH = import.meta.env.VITE_IM_PATH_FIlE;
 // 路由 & store
@@ -183,6 +185,7 @@ const router = useRouter();
 const route = useRoute();
 const wsStore = useWebSocketStore();
 const walletStore = useWalletStore();
+const webRTCStore = useWebRTCStore()
 
 // 输入框文本
 const text = ref("");
@@ -391,6 +394,18 @@ const beforeRead = (file) => {
   return true;
 };
 
+// 开启语音电话
+const startAudioOnline = () => { 
+  webRTCStore.initConnection(
+    MESSAGE_TYPE_USER,
+    {
+      fromUsername: walletStore.username,
+      from: walletStore.account,
+      to:  route.query.uuid,
+    }
+  )
+};
+
 // 时间格式化
 const formatTime = (timestamp) => {
   const date = new Date(timestamp);