[+]
This commit is contained in:
@@ -1,29 +1,3 @@
|
||||
# client
|
||||
# Web终端
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
注意部分`API`需要`localhost`或者`https`
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.2.45",
|
||||
"moment": "^2.29.4"
|
||||
"moment": "^2.29.4",
|
||||
"element-plus": "^2.2.29",
|
||||
"mediasoup-client": "^3.6.77"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^4.0.0",
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
<template></template>
|
||||
<!-- 桃夭 -->
|
||||
<template>
|
||||
<SettingRoom :roomVisible="roomVisible" :taoyao="taoyao"></SettingRoom>
|
||||
<SettingSignal :signalVisible="signalVisible" @connectSignal="connectSignal"></SettingSignal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Logger } from "./components/Logger.js";
|
||||
import { Taoyao } from "./components/Taoyao.js";
|
||||
import SettingRoom from "./components/SettingRoom.vue";
|
||||
import SettingSignal from "./components/SettingSignal.vue";
|
||||
|
||||
export default {
|
||||
name: "taoyao",
|
||||
name: "Taoyao",
|
||||
data() {
|
||||
return {
|
||||
logger: null,
|
||||
taoyao: null,
|
||||
logger: {},
|
||||
taoyao: {},
|
||||
roomVisible: false,
|
||||
signalVisible: true,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 填写、密码、帐号
|
||||
this.logger = new Logger();
|
||||
this.taoyao = new Taoyao();
|
||||
this.logger.info("桃夭终端启动");
|
||||
this.taoyao.buildChannel(this.callback);
|
||||
this.logger.info("桃夭终端开始启动");
|
||||
},
|
||||
beforeDestroy() {},
|
||||
methods: {
|
||||
connectSignal: function() {
|
||||
let self = this;
|
||||
self.signalVisible = false;
|
||||
self.taoyao.buildChannel(self.callback);
|
||||
},
|
||||
/**
|
||||
* 信令回调
|
||||
*
|
||||
@@ -30,10 +40,22 @@ export default {
|
||||
*/
|
||||
callback: function (data) {
|
||||
let self = this;
|
||||
switch (data.header.snail) {
|
||||
switch (data.header.signal) {
|
||||
case "client::config":
|
||||
self.roomVisible = true;
|
||||
break;
|
||||
case "client::register":
|
||||
if(data.code === '3401') {
|
||||
self.signalVisible = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
SettingRoom,
|
||||
SettingSignal,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import "./base.css";
|
||||
@import "./font.taoyao.css";
|
||||
@import "./font.css";
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
|
||||
@@ -7,19 +7,27 @@
|
||||
*/
|
||||
const config = {
|
||||
// 终端标识
|
||||
sn: null,
|
||||
sn: "taoyao",
|
||||
// 信令服务地址
|
||||
host: "localhost",
|
||||
port: "8888",
|
||||
// 终端名称
|
||||
name: "taoyao-client-web",
|
||||
// 终端版本
|
||||
version: "1.0.0",
|
||||
// 日志级别
|
||||
logLevel: "DEBUG",
|
||||
// 信令服务地址
|
||||
host: "localhost",
|
||||
port: "8888",
|
||||
// 帐号密码
|
||||
username: "taoyao",
|
||||
password: "taoyao",
|
||||
signal: function () {
|
||||
return `wss://${this.host}:${this.port}/websocket.signal`;
|
||||
},
|
||||
// 媒体配置
|
||||
audio: {},
|
||||
video: {},
|
||||
// WebRTC配置
|
||||
webrtc: {},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -44,13 +52,13 @@ const protocol = {
|
||||
/**
|
||||
* 生成信令消息
|
||||
*
|
||||
* @param {*} id ID
|
||||
* @param {*} body 信令消息
|
||||
* @param {*} signal 信令标识
|
||||
* @param {*} body 信令消息
|
||||
* @param {*} id ID
|
||||
*
|
||||
* @returns 信令消息
|
||||
*/
|
||||
buildMessage: function (id, body, signal) {
|
||||
buildMessage: function (signal, body = {}, id) {
|
||||
let message = {
|
||||
header: {
|
||||
v: config.version,
|
||||
|
||||
@@ -1,2 +1,23 @@
|
||||
<!-- 本地终端 -->
|
||||
<template></template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@vue/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
// 本地视频
|
||||
this._externalVideo = document.createElement("video");
|
||||
|
||||
this._externalVideo.controls = true;
|
||||
this._externalVideo.muted = true;
|
||||
this._externalVideo.loop = true;
|
||||
this._externalVideo.setAttribute("playsinline", "");
|
||||
this._externalVideo.src = EXTERNAL_VIDEO_SRC;
|
||||
|
||||
this._externalVideo
|
||||
.play()
|
||||
.catch((error) => logger.warn("externalVideo.play() failed:%o", error));
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
100
taoyao-client-web/src/components/SettingRoom.vue
Normal file
100
taoyao-client-web/src/components/SettingRoom.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<!-- 房间设置 -->
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="localVisible"
|
||||
@open="init"
|
||||
width="30%"
|
||||
:show-close="false"
|
||||
center
|
||||
>
|
||||
<el-form ref="SettingRoomForm" :model="room">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="进入房间" name="enter">
|
||||
<el-form-item label="房间标识">
|
||||
<el-select v-model="room.id" placeholder="房间标识">
|
||||
<el-option
|
||||
v-for="value in rooms"
|
||||
:key="value.id"
|
||||
:label="value.name"
|
||||
:value="value.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="创建房间" name="create">
|
||||
<el-form-item label="媒体服务">
|
||||
<el-select v-model="room.mediasoup" placeholder="媒体服务">
|
||||
<el-option
|
||||
v-for="mediasoup in config.webrtc.mediasoupList"
|
||||
:key="mediasoup.name"
|
||||
:label="mediasoup.name"
|
||||
:value="mediasoup.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="房间名称">
|
||||
<el-input v-model="room.name" placeholder="房间名称" />
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-form-item label="房间密码">
|
||||
<el-input v-model="room.password" placeholder="房间密码" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="setting">设置</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { config, protocol } from "./Config.js";
|
||||
|
||||
export default {
|
||||
name: "SettingRoom",
|
||||
data() {
|
||||
return {
|
||||
config,
|
||||
room: {},
|
||||
rooms: [],
|
||||
activeName: "enter",
|
||||
localVisible: false,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
taoyao: {},
|
||||
roomVisible: false,
|
||||
},
|
||||
watch: {
|
||||
roomVisible() {
|
||||
this.localVisible = this.roomVisible;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
let response = await this.taoyao.request(
|
||||
protocol.buildMessage("room::list")
|
||||
);
|
||||
this.rooms = response.body;
|
||||
},
|
||||
async setting() {
|
||||
this.localVisible = false;
|
||||
if (this.activeName === "enter") {
|
||||
await this.taoyao.request(
|
||||
protocol.buildMessage("room::enter", {
|
||||
sn: config.sn,
|
||||
...this.room,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
await this.taoyao.request(
|
||||
protocol.buildMessage("room::create", {
|
||||
sn: config.sn,
|
||||
...this.room,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
53
taoyao-client-web/src/components/SettingSignal.vue
Normal file
53
taoyao-client-web/src/components/SettingSignal.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<!-- 终端设置 -->
|
||||
<template>
|
||||
<el-dialog v-model="localVisible" title="终端设置" width="30%" :show-close="false" center>
|
||||
<el-form ref="SettingSignalForm">
|
||||
<el-form-item label="终端名称">
|
||||
<el-input v-model="config.sn" placeholder="终端名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="信令帐号">
|
||||
<el-input v-model="config.username" placeholder="信令帐号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="信令密码">
|
||||
<el-input v-model="config.password" placeholder="信令密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="信令地址">
|
||||
<el-input v-model="config.host" placeholder="信令地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="信令端口">
|
||||
<el-input v-model="config.port" placeholder="信令端口" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="setting">设置</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { config } from "./Config.js";
|
||||
|
||||
export default {
|
||||
name: "SettingSignal",
|
||||
data: () => {
|
||||
return {
|
||||
config,
|
||||
localVisible: true,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
signalVisible: true,
|
||||
},
|
||||
watch: {
|
||||
signalVisible() {
|
||||
this.localVisible = this.signalVisible;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setting: function () {
|
||||
this.localVisible = false;
|
||||
this.$emit("connectSignal");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -3,14 +3,19 @@
|
||||
*/
|
||||
import { Logger } from "./Logger.js";
|
||||
import { TaoyaoClient } from "./TaoyaoClient.js";
|
||||
import { config, protocol, defaultAudioConfig, defaultVideoConfig } from "./Config.js";
|
||||
import * as mediasoupClient from 'mediasoup-client';
|
||||
import {
|
||||
config,
|
||||
protocol,
|
||||
defaultAudioConfig,
|
||||
defaultVideoConfig,
|
||||
} from "./Config.js";
|
||||
|
||||
// 日志
|
||||
const logger = new Logger();
|
||||
|
||||
/**
|
||||
* 信令通道
|
||||
* TODO:获取IP/MAC/信号强度
|
||||
*/
|
||||
const signalChannel = {
|
||||
// 桃夭
|
||||
@@ -27,6 +32,8 @@ const signalChannel = {
|
||||
heartbeatTime: 30 * 1000,
|
||||
// 心跳定时器
|
||||
heartbeatTimer: null,
|
||||
// 是否重连
|
||||
reconnection: true,
|
||||
// 重连定时器
|
||||
reconnectTimer: null,
|
||||
// 防止重复重连
|
||||
@@ -72,10 +79,11 @@ const signalChannel = {
|
||||
*
|
||||
* @returns Promise
|
||||
*/
|
||||
connect: function (address, callback, reconnection = true) {
|
||||
connect: async function (address, callback, reconnection = true) {
|
||||
let self = this;
|
||||
self.address = address;
|
||||
self.callback = callback;
|
||||
self.reconnection = reconnection;
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.debug("连接信令通道", address);
|
||||
self.channel = new WebSocket(address);
|
||||
@@ -85,8 +93,7 @@ const signalChannel = {
|
||||
const battery = await navigator.getBattery();
|
||||
self.push(
|
||||
protocol.buildMessage("client::register", {
|
||||
ip: null,
|
||||
mac: null,
|
||||
ip: 'localhost',
|
||||
signal: 100,
|
||||
battery: battery.level * 100,
|
||||
charging: battery.charging,
|
||||
@@ -103,14 +110,14 @@ const signalChannel = {
|
||||
};
|
||||
self.channel.onclose = function (e) {
|
||||
logger.error("信令通道关闭", self.channel, e);
|
||||
if (reconnection) {
|
||||
if (self.reconnection) {
|
||||
self.reconnect();
|
||||
}
|
||||
reject(e);
|
||||
};
|
||||
self.channel.onerror = function (e) {
|
||||
logger.error("信令通道异常", self.channel, e);
|
||||
if (reconnection) {
|
||||
if (self.reconnection) {
|
||||
self.reconnect();
|
||||
}
|
||||
reject(e);
|
||||
@@ -122,7 +129,7 @@ const signalChannel = {
|
||||
* 3. 如果前面所有回调没有返回true执行默认回调。
|
||||
*/
|
||||
self.channel.onmessage = function (e) {
|
||||
console.debug("信令通道消息", e.data);
|
||||
logger.debug("信令通道消息", e.data);
|
||||
let done = false;
|
||||
let data = JSON.parse(e.data);
|
||||
// 请求回调
|
||||
@@ -134,7 +141,7 @@ const signalChannel = {
|
||||
}
|
||||
}
|
||||
// 全局回调
|
||||
if (self.callback) {
|
||||
if (!done && self.callback) {
|
||||
done = self.callback(data);
|
||||
}
|
||||
// 默认回调
|
||||
@@ -163,39 +170,67 @@ const signalChannel = {
|
||||
}
|
||||
// 打开定时重连
|
||||
self.reconnectTimer = setTimeout(function () {
|
||||
console.info("信令通道重连", self.address);
|
||||
logger.info("信令通道重连", self.address);
|
||||
self.connect(self.address, self.callback, true);
|
||||
self.lockReconnect = false;
|
||||
}, self.connectionTimeout);
|
||||
if (self.connectionTimeout >= self.maxReconnectionDelay) {
|
||||
self.connectionTimeout = self.maxReconnectionDelay;
|
||||
} else {
|
||||
self.connectionTimeout = self.connectionTimeout * self.reconnectionDelayGrowFactor;
|
||||
self.connectionTimeout =
|
||||
self.connectionTimeout * self.reconnectionDelayGrowFactor;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* 异步请求
|
||||
*
|
||||
* @param {*} data 消息内容
|
||||
* @param {*} callback 注册回调
|
||||
*/
|
||||
push: function (data, callback) {
|
||||
// 注册回调
|
||||
if (data && callback) {
|
||||
this.callbackMapping.set(data.header.id, callback);
|
||||
let self = this;
|
||||
if (callback) {
|
||||
self.callbackMapping.set(data.header.id, callback);
|
||||
}
|
||||
// 发送消息
|
||||
if (data && data.header) {
|
||||
this.channel.send(JSON.stringify(data));
|
||||
} else {
|
||||
this.channel.send(data);
|
||||
}
|
||||
self.channel.send(JSON.stringify(data));
|
||||
},
|
||||
/**
|
||||
* 同步请求
|
||||
*
|
||||
* @param {*} data 消息内容
|
||||
*
|
||||
* @returns Promise
|
||||
*/
|
||||
request: async function(data) {
|
||||
let self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
let callback = false;
|
||||
// 设置回调
|
||||
self.callbackMapping.set(data.header.id, (response) => {
|
||||
callback = true;
|
||||
resolve(response);
|
||||
return true;
|
||||
});
|
||||
// 发送请求
|
||||
self.channel.send(JSON.stringify(data));
|
||||
// 设置超时
|
||||
setTimeout(() => {
|
||||
if(!callback) {
|
||||
reject("请求超时", data);
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 关闭通道
|
||||
*/
|
||||
close: function () {
|
||||
clearTimeout(this.heartbeatTimer);
|
||||
let self = this;
|
||||
self.reconnection = false;
|
||||
self.channel.close();
|
||||
clearTimeout(self.heartbeatTimer);
|
||||
},
|
||||
/**
|
||||
* 默认回调
|
||||
@@ -203,10 +238,17 @@ const signalChannel = {
|
||||
* @param {*} data 消息内容
|
||||
*/
|
||||
defaultCallback: function (data) {
|
||||
console.debug("没有适配信令消息默认处理", data);
|
||||
let self = this;
|
||||
logger.debug("没有适配信令消息默认处理", data);
|
||||
switch (data.header.signal) {
|
||||
case "client::config":
|
||||
self.defaultClientConfig(data);
|
||||
break;
|
||||
case "client::register":
|
||||
logger.info("桃夭终端注册成功");
|
||||
break;
|
||||
case "platform::error":
|
||||
console.error("信令发生错误", data);
|
||||
logger.error("信令发生错误", data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@@ -216,24 +258,10 @@ const signalChannel = {
|
||||
* @param {*} data 消息内容
|
||||
*/
|
||||
defaultClientConfig: function (data) {
|
||||
let self = this;
|
||||
// 配置终端
|
||||
self.taoyao
|
||||
.configMedia(data.body.media.audio, data.body.media.video)
|
||||
.configWebrtc(data.body.webrtc);
|
||||
// 打开媒体通道
|
||||
let videoId = self.taoyao.videoId;
|
||||
if (videoId) {
|
||||
self.taoyao
|
||||
.buildLocalMedia()
|
||||
.then((stream) => {
|
||||
self.taoyao.buildMediaChannel(videoId, stream);
|
||||
})
|
||||
.catch((e) => console.error("打开终端媒体失败", e));
|
||||
console.debug("自动打开媒体通道", videoId);
|
||||
} else {
|
||||
console.debug("没有配置本地媒体信息跳过自动打开媒体通道");
|
||||
}
|
||||
config.webrtc = data.body.webrtc;
|
||||
config.audio = { ...config.defaultAudioConfig, ...data.body.media.audio };
|
||||
config.video = { ...config.defaultVideoConfig, ...data.body.media.video };
|
||||
logger.info("终端配置", config.audio, config.video, config.webrtc);
|
||||
},
|
||||
/**
|
||||
* 默认终端重启回调
|
||||
@@ -241,7 +269,7 @@ const signalChannel = {
|
||||
* @param {*} data 消息内容
|
||||
*/
|
||||
defaultClientReboot: function (data) {
|
||||
console.info("重启终端");
|
||||
logger.info("重启终端");
|
||||
location.reload();
|
||||
},
|
||||
};
|
||||
@@ -250,63 +278,89 @@ const signalChannel = {
|
||||
* 桃夭
|
||||
*/
|
||||
class Taoyao {
|
||||
// 发送信令
|
||||
push = null;
|
||||
// 请求信令
|
||||
request = null;
|
||||
// 本地视频
|
||||
localVideo = null;
|
||||
// 本地终端
|
||||
localClient;
|
||||
// 远程终端
|
||||
remoteClientList;
|
||||
// 设备状态
|
||||
audioEnabled = true;
|
||||
videoEnabled = true;
|
||||
// 媒体配置
|
||||
audioConfig = defaultAudioConfig;
|
||||
videoConfig = defaultVideoConfig;
|
||||
// 媒体通道
|
||||
transSend;
|
||||
transRecv;
|
||||
// 发送信令
|
||||
push = null;
|
||||
// 信令通道
|
||||
signalChannel = null;
|
||||
sendTransport = null;
|
||||
recvTransport = null;
|
||||
// 信令通道
|
||||
signalChannel = null;
|
||||
// 媒体设备
|
||||
mediasoupDevice = null;
|
||||
// 是否消费
|
||||
consume = true;
|
||||
// 是否生产
|
||||
produce = true;
|
||||
// 是否生产音频
|
||||
audioProduce = true && this.produce;
|
||||
// 是否生成视频
|
||||
videoProduce = true && this.produce;
|
||||
// 音频生产者
|
||||
audioProducer = null;
|
||||
// 视频生产者
|
||||
videoProducer = null;
|
||||
// 消费者
|
||||
consumers = new Map();
|
||||
// 数据消费者
|
||||
dataConsumers = new Map();
|
||||
|
||||
/**
|
||||
* 媒体配置
|
||||
*
|
||||
* @param {*} audio
|
||||
* @param {*} video
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
configMedia = function(audio = {}, video = {}) {
|
||||
this.audioConfig = {...this.audioConfig, ...audio};
|
||||
this.videoConfig = {...this.videoConfig, ...video};
|
||||
console.debug('终端媒体配置', this.audioConfig, this.videoConfig);
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* WebRTC配置
|
||||
*
|
||||
* @param {*} config
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
configWebrtc = function(config = {}) {
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* 打开信令通道
|
||||
*
|
||||
* @param {*} callback
|
||||
*
|
||||
* @returns
|
||||
*
|
||||
* @param {*} callback
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
buildChannel = function(callback) {
|
||||
signalChannel.taoyao = this;
|
||||
this.signalChannel = signalChannel;
|
||||
// 不能直接this.push = this.signalChannel.push这样导致this对象错误
|
||||
this.push = function(data, pushCallback) {
|
||||
this.signalChannel.push(data, pushCallback);
|
||||
};
|
||||
return this.signalChannel.connect(config.signal(), callback);
|
||||
};
|
||||
buildChannel = async function (callback) {
|
||||
signalChannel.taoyao = this;
|
||||
this.signalChannel = signalChannel;
|
||||
// 不能直接this.push = this.signalChannel.push这样导致this对象错误
|
||||
this.push = function (data, pushCallback) {
|
||||
this.signalChannel.push(data, pushCallback);
|
||||
};
|
||||
this.request = async function(data) {
|
||||
return await this.signalChannel.request(data);
|
||||
}
|
||||
return this.signalChannel.connect(config.signal(), callback);
|
||||
};
|
||||
/**
|
||||
* 设置本地媒体
|
||||
*/
|
||||
buildLocal = function() {
|
||||
new mediasoupClient.Device();
|
||||
};
|
||||
/**
|
||||
* 打开媒体通道
|
||||
*/
|
||||
buildMediaTransport = function() {
|
||||
let self = this;
|
||||
// 释放资源
|
||||
self.close();
|
||||
|
||||
}
|
||||
/**
|
||||
* 关闭
|
||||
*/
|
||||
close = function() {
|
||||
let self = this;
|
||||
if(self.sendTransport) {
|
||||
self.sendTransport.close();
|
||||
}
|
||||
if(self.recvTransport) {
|
||||
self.recvTransport.close();
|
||||
}
|
||||
if(self.signalChannel) {
|
||||
self.signalChannel.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { Taoyao };
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import "./assets/main.css";
|
||||
import ElementPlus from "element-plus";
|
||||
import "element-plus/dist/index.css";
|
||||
|
||||
createApp(App).mount("#app");
|
||||
const app = createApp(App);
|
||||
app.use(ElementPlus);
|
||||
app.mount("#app");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
|
||||
Reference in New Issue
Block a user