|
@@ -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";
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+});
|