[*]
This commit is contained in:
@@ -1,11 +1,39 @@
|
||||
<script setup>
|
||||
<template></template>
|
||||
|
||||
<script>
|
||||
import { Logger } from "./components/Logger.js";
|
||||
import { Taoyao } from "./components/Taoyao.js";
|
||||
|
||||
export default {
|
||||
name: "taoyao",
|
||||
data() {
|
||||
return {
|
||||
logger: null,
|
||||
taoyao: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 填写、密码、帐号
|
||||
this.logger = new Logger();
|
||||
this.taoyao = new Taoyao();
|
||||
this.logger.info("桃夭终端启动");
|
||||
this.taoyao.buildChannel(this.callback);
|
||||
},
|
||||
beforeDestroy() {},
|
||||
methods: {
|
||||
/**
|
||||
* 信令回调
|
||||
*
|
||||
* @param {*} data 消息
|
||||
*
|
||||
* @return 是否继续执行
|
||||
*/
|
||||
callback: function (data) {
|
||||
let self = this;
|
||||
switch (data.header.snail) {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header></header>
|
||||
<main></main>
|
||||
<footer></footer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
@font-face {
|
||||
font-family: taoyao;
|
||||
src: url(./taoyao.ttf?oc7k8r) format("truetype"), url(./taoyao.woff?oc7k8r) format("woff"), url(./taoyao.svg?oc7k8r#taoyao) format("svg");
|
||||
src:
|
||||
url(./taoyao.ttf?oc7k8r) format("truetype"),
|
||||
url(./taoyao.woff?oc7k8r) format("woff"),
|
||||
url(./taoyao.svg?oc7k8r#taoyao) format("svg");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 桃夭配置
|
||||
*/
|
||||
|
||||
/**
|
||||
* 信令配置
|
||||
*/
|
||||
const config = {
|
||||
// 终端标识
|
||||
sn: null,
|
||||
// 终端名称
|
||||
name: "taoyao-client-web",
|
||||
// 终端版本
|
||||
version: "1.0.0",
|
||||
// 日志级别
|
||||
logLevel: "DEBUG",
|
||||
// 信令服务地址
|
||||
host: "localhost",
|
||||
port: "8888",
|
||||
signal: function () {
|
||||
return `wss://${this.host}:${this.port}/websocket.signal`;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 信令协议
|
||||
*/
|
||||
const protocol = {
|
||||
// 当前索引
|
||||
index: 100000,
|
||||
// 最小索引
|
||||
minIndex: 100000,
|
||||
// 最大索引
|
||||
maxIndex: 999999,
|
||||
/**
|
||||
* @returns 索引
|
||||
*/
|
||||
buildId: function () {
|
||||
if (this.index++ >= this.maxIndex) {
|
||||
this.index = this.minIndex;
|
||||
}
|
||||
return Date.now() + "" + this.index;
|
||||
},
|
||||
/**
|
||||
* 生成信令消息
|
||||
*
|
||||
* @param {*} id ID
|
||||
* @param {*} body 信令消息
|
||||
* @param {*} signal 信令标识
|
||||
*
|
||||
* @returns 信令消息
|
||||
*/
|
||||
buildMessage: function (id, body, signal) {
|
||||
let message = {
|
||||
header: {
|
||||
v: config.version,
|
||||
id: id || this.buildId(),
|
||||
sn: config.sn,
|
||||
signal: signal,
|
||||
},
|
||||
body: body,
|
||||
};
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 默认音频配置
|
||||
*/
|
||||
const defaultAudioConfig = {
|
||||
// 设备
|
||||
// deviceId : '',
|
||||
// 音量:0~1
|
||||
volume: 0.5,
|
||||
// 延迟大小(单位毫秒):500毫秒以内较好
|
||||
latency: 0.4,
|
||||
// 采样数:16
|
||||
sampleSize: 16,
|
||||
// 采样率:8000|16000|32000|48000
|
||||
sampleRate: 32000,
|
||||
// 声道数量:1|2
|
||||
channelCount: 1,
|
||||
// 是否开启自动增益:true|false
|
||||
autoGainControl: false,
|
||||
// 是否开启降噪功能:true|false
|
||||
noiseSuppression: true,
|
||||
// 是否开启回音消除:true|false
|
||||
echoCancellation: true,
|
||||
// 消除回音方式:system|browser
|
||||
echoCancellationType: "system",
|
||||
};
|
||||
|
||||
/**
|
||||
* 默认视频配置
|
||||
*/
|
||||
const defaultVideoConfig = {
|
||||
// 设备
|
||||
// deviceId: '',
|
||||
// 宽度
|
||||
width: 1280,
|
||||
// 高度
|
||||
height: 720,
|
||||
// 帧率
|
||||
frameRate: 24,
|
||||
// 选摄像头:user|left|right|environment
|
||||
facingMode: "environment",
|
||||
};
|
||||
|
||||
/**
|
||||
* 默认RTCPeerConnection配置
|
||||
*/
|
||||
const defaultRTCPeerConnectionConfig = {
|
||||
// ICE代理的服务器
|
||||
iceServers: null,
|
||||
// 传输通道绑定策略:balanced|max-compat|max-bundle
|
||||
bundlePolicy: "balanced",
|
||||
// RTCP多路复用策略:require|negotiate
|
||||
rtcpMuxPolicy: "require",
|
||||
// ICE传输策略:all|relay
|
||||
iceTransportPolicy: "all",
|
||||
// ICE候选个数
|
||||
iceCandidatePoolSize: 8,
|
||||
};
|
||||
|
||||
export {
|
||||
config,
|
||||
protocol,
|
||||
defaultAudioConfig,
|
||||
defaultVideoConfig,
|
||||
defaultRTCPeerConnectionConfig,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
<!-- 本地终端 -->
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<template></template>
|
||||
|
||||
94
taoyao-client-web/src/components/Logger.js
Normal file
94
taoyao-client-web/src/components/Logger.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 日志
|
||||
*/
|
||||
import moment from "moment";
|
||||
import { config } from "./Config.js";
|
||||
|
||||
/**
|
||||
* 日志
|
||||
*/
|
||||
class Logger {
|
||||
constructor(prefix = "") {
|
||||
if (prefix) {
|
||||
this.name = this.name + " : " + prefix;
|
||||
}
|
||||
}
|
||||
|
||||
// 名称
|
||||
name = config.name;
|
||||
// 级别
|
||||
level = ["DEBUG", "INFO", "WARN", "ERROR", "OFF"];
|
||||
// 级别索引
|
||||
levelIndex = this.level.indexOf(config.logLevel.toUpperCase());
|
||||
|
||||
/**
|
||||
* debug
|
||||
*
|
||||
* @param {...any} args 参数
|
||||
*
|
||||
* @returns this
|
||||
*/
|
||||
debug(...args) {
|
||||
return this.log(console.debug, "37m", "DEBUG", args);
|
||||
}
|
||||
|
||||
/**
|
||||
* info
|
||||
*
|
||||
* @param {...any} args 参数
|
||||
*
|
||||
* @returns this
|
||||
*/
|
||||
info(...args) {
|
||||
return this.log(console.info, "32m", "INFO", args);
|
||||
}
|
||||
|
||||
/**
|
||||
* warn
|
||||
*
|
||||
* @param {...any} args 参数
|
||||
*
|
||||
* @returns this
|
||||
*/
|
||||
warn(...args) {
|
||||
return this.log(console.warn, "33m", "WARN", args);
|
||||
}
|
||||
|
||||
/**
|
||||
* error
|
||||
*
|
||||
* @param {...any} args 参数
|
||||
*
|
||||
* @returns this
|
||||
*/
|
||||
error(...args) {
|
||||
return this.log(console.error, "31m", "ERROR", args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志
|
||||
*
|
||||
* @param {*} out 输出
|
||||
* @param {*} color 颜色
|
||||
* @param {*} level 级别
|
||||
* @param {*} args 参数
|
||||
*
|
||||
* @returns this
|
||||
*/
|
||||
log(out, color, level, args) {
|
||||
if (!args || this.level.indexOf(level) < this.levelIndex) {
|
||||
return this;
|
||||
}
|
||||
if (args.length > 1 && args[0].length > 0) {
|
||||
out(`\x1B[${color}${this.name} ${moment().format("yyyy-MM-DD HH:mm:ss")} : [${level.padEnd(5, " ")}] :\x1B[0m`, args);
|
||||
} else if (args.length === 1 && args[0].length > 0) {
|
||||
out(`\x1B[${color}${this.name} ${moment().format("yyyy-MM-DD HH:mm:ss")} : [${level.padEnd(5, " ")}] :\x1B[0m`, args);
|
||||
} else {
|
||||
// 其他情况直接输出换行
|
||||
out("");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export { Logger };
|
||||
@@ -1,7 +1,2 @@
|
||||
<!-- 远程终端 -->
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<template></template>
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* 桃夭
|
||||
*/
|
||||
import { Logger } from "./Logger.js";
|
||||
import { TaoyaoClient } from "./TaoyaoClient.js";
|
||||
import { config, protocol, defaultAudioConfig, defaultVideoConfig } from "./Config.js";
|
||||
|
||||
// 日志
|
||||
const logger = new Logger();
|
||||
|
||||
/**
|
||||
* 信令通道
|
||||
* TODO:获取IP/MAC/信号强度
|
||||
*/
|
||||
const signalChannel = {
|
||||
// 桃夭
|
||||
taoyao: null,
|
||||
// 通道
|
||||
channel: null,
|
||||
// 地址
|
||||
address: null,
|
||||
// 回调
|
||||
callback: null,
|
||||
// 回调事件
|
||||
callbackMapping: new Map(),
|
||||
// 心跳时间
|
||||
heartbeatTime: 30 * 1000,
|
||||
// 心跳定时器
|
||||
heartbeatTimer: null,
|
||||
// 重连定时器
|
||||
reconnectTimer: null,
|
||||
// 防止重复重连
|
||||
lockReconnect: false,
|
||||
// 当前重连时间
|
||||
connectionTimeout: 5 * 1000,
|
||||
// 最小重连时间
|
||||
minReconnectionDelay: 5 * 1000,
|
||||
// 最大重连时间
|
||||
maxReconnectionDelay: 60 * 1000,
|
||||
// 重连失败时间增长倍数
|
||||
reconnectionDelayGrowFactor: 2,
|
||||
/**
|
||||
* 心跳
|
||||
*/
|
||||
heartbeat: function () {
|
||||
let self = this;
|
||||
if (self.heartbeatTimer) {
|
||||
clearTimeout(self.heartbeatTimer);
|
||||
}
|
||||
self.heartbeatTimer = setTimeout(async function () {
|
||||
if (self.channel && self.channel.readyState === WebSocket.OPEN) {
|
||||
const battery = await navigator.getBattery();
|
||||
self.push(
|
||||
protocol.buildMessage("client::heartbeat", {
|
||||
signal: 100,
|
||||
battery: battery.level * 100,
|
||||
charging: battery.charging,
|
||||
})
|
||||
);
|
||||
self.heartbeat();
|
||||
} else {
|
||||
logger.warn("发送心跳失败", self.channel);
|
||||
}
|
||||
}, self.heartbeatTime);
|
||||
},
|
||||
/**
|
||||
* 连接
|
||||
*
|
||||
* @param {*} address 地址
|
||||
* @param {*} callback 回调
|
||||
* @param {*} reconnection 是否重连
|
||||
*
|
||||
* @returns Promise
|
||||
*/
|
||||
connect: function (address, callback, reconnection = true) {
|
||||
let self = this;
|
||||
self.address = address;
|
||||
self.callback = callback;
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.debug("连接信令通道", address);
|
||||
self.channel = new WebSocket(address);
|
||||
self.channel.onopen = async function (e) {
|
||||
logger.debug("打开信令通道", e);
|
||||
// 注册终端
|
||||
const battery = await navigator.getBattery();
|
||||
self.push(
|
||||
protocol.buildMessage("client::register", {
|
||||
ip: null,
|
||||
mac: null,
|
||||
signal: 100,
|
||||
battery: battery.level * 100,
|
||||
charging: battery.charging,
|
||||
username: config.username,
|
||||
password: config.password,
|
||||
})
|
||||
);
|
||||
// 重置时间
|
||||
self.connectionTimeout = self.minReconnectionDelay;
|
||||
// 开始心跳
|
||||
self.heartbeat();
|
||||
// 成功回调
|
||||
resolve(e);
|
||||
};
|
||||
self.channel.onclose = function (e) {
|
||||
logger.error("信令通道关闭", self.channel, e);
|
||||
if (reconnection) {
|
||||
self.reconnect();
|
||||
}
|
||||
reject(e);
|
||||
};
|
||||
self.channel.onerror = function (e) {
|
||||
logger.error("信令通道异常", self.channel, e);
|
||||
if (reconnection) {
|
||||
self.reconnect();
|
||||
}
|
||||
reject(e);
|
||||
};
|
||||
/**
|
||||
* 回调策略:
|
||||
* 1. 如果注册请求回调,同时执行结果返回true不再执行后面所有回调。
|
||||
* 2. 如果注册全局回调,同时执行结果返回true不再执行后面所有回调。
|
||||
* 3. 如果前面所有回调没有返回true执行默认回调。
|
||||
*/
|
||||
self.channel.onmessage = function (e) {
|
||||
console.debug("信令通道消息", e.data);
|
||||
let done = false;
|
||||
let data = JSON.parse(e.data);
|
||||
// 请求回调
|
||||
if (self.callbackMapping.has(data.header.id)) {
|
||||
try {
|
||||
done = self.callbackMapping.get(data.header.id)(data);
|
||||
} finally {
|
||||
self.callbackMapping.delete(data.header.id);
|
||||
}
|
||||
}
|
||||
// 全局回调
|
||||
if (self.callback) {
|
||||
done = self.callback(data);
|
||||
}
|
||||
// 默认回调
|
||||
if (!done) {
|
||||
self.defaultCallback(data);
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 重连
|
||||
*/
|
||||
reconnect: function () {
|
||||
let self = this;
|
||||
if (self.lockReconnect) {
|
||||
return;
|
||||
}
|
||||
self.lockReconnect = true;
|
||||
// 关闭旧的通道
|
||||
if (self.channel && self.channel.readyState === WebSocket.OPEN) {
|
||||
self.channel.close();
|
||||
self.channel = null;
|
||||
}
|
||||
if (self.reconnectTimer) {
|
||||
clearTimeout(self.reconnectTimer);
|
||||
}
|
||||
// 打开定时重连
|
||||
self.reconnectTimer = setTimeout(function () {
|
||||
console.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;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param {*} data 消息内容
|
||||
* @param {*} callback 注册回调
|
||||
*/
|
||||
push: function (data, callback) {
|
||||
// 注册回调
|
||||
if (data && callback) {
|
||||
this.callbackMapping.set(data.header.id, callback);
|
||||
}
|
||||
// 发送消息
|
||||
if (data && data.header) {
|
||||
this.channel.send(JSON.stringify(data));
|
||||
} else {
|
||||
this.channel.send(data);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 关闭通道
|
||||
*/
|
||||
close: function () {
|
||||
clearTimeout(this.heartbeatTimer);
|
||||
},
|
||||
/**
|
||||
* 默认回调
|
||||
*
|
||||
* @param {*} data 消息内容
|
||||
*/
|
||||
defaultCallback: function (data) {
|
||||
console.debug("没有适配信令消息默认处理", data);
|
||||
switch (data.header.signal) {
|
||||
case "platform::error":
|
||||
console.error("信令发生错误", data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 默认配置回调
|
||||
*
|
||||
* @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("没有配置本地媒体信息跳过自动打开媒体通道");
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 默认终端重启回调
|
||||
*
|
||||
* @param {*} data 消息内容
|
||||
*/
|
||||
defaultClientReboot: function (data) {
|
||||
console.info("重启终端");
|
||||
location.reload();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 桃夭
|
||||
*/
|
||||
class Taoyao {
|
||||
// 本地终端
|
||||
localClient;
|
||||
// 远程终端
|
||||
remoteClientList;
|
||||
// 设备状态
|
||||
audioEnabled = true;
|
||||
videoEnabled = true;
|
||||
// 媒体配置
|
||||
audioConfig = defaultAudioConfig;
|
||||
videoConfig = defaultVideoConfig;
|
||||
// 媒体通道
|
||||
transSend;
|
||||
transRecv;
|
||||
// 发送信令
|
||||
push = null;
|
||||
// 信令通道
|
||||
signalChannel = null;
|
||||
/**
|
||||
* 媒体配置
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
export { Taoyao };
|
||||
|
||||
6
taoyao-client-web/src/components/TaoyaoClient.js
Normal file
6
taoyao-client-web/src/components/TaoyaoClient.js
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 桃夭终端
|
||||
*/
|
||||
class TaoyaoClient {}
|
||||
|
||||
export { TaoyaoClient };
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import "./assets/main.css";
|
||||
|
||||
import './assets/main.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
createApp(App).mount("#app");
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,36 +0,0 @@
|
||||
@charset "UTF-8";
|
||||
/**文本选择*/
|
||||
::selection{background:#222;color:#fff;}
|
||||
/**字体大小*/
|
||||
@media screen and (min-width:800px){html{font-size:16px;}}
|
||||
@media screen and (min-width:1200px){html{font-size:18px;}}
|
||||
@media screen and (min-width:1600px){html{font-size:20px;}}
|
||||
/**默认样式*/
|
||||
*{margin:0;padding:0;border:none;outline:none;box-sizing:content-box;}
|
||||
html{background:#EBEBEB;}
|
||||
html,body{font-family:Arial,Consolas,SimSun,"宋体";color:#222;font-weight:normal;}
|
||||
body{width:100%;height:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);font-size:1rem;line-height:1.4em;}
|
||||
a{color:#1155AA;text-decoration:none;}
|
||||
a:link{text-decoration:none;}
|
||||
a:hover{color:#4477EE;text-decoration:none;}
|
||||
a:visited{text-decoration:none;}
|
||||
img{border:0;}
|
||||
ol,ul,li{list-style:none;}
|
||||
input[type=text],textarea{box-shadow:0px 0px 3px 0px rgba(0,0,0,0.1) inset;border:1px solid rgba(0,0,0,0.1)!important;}
|
||||
input[type=text]:focus,textarea:focus,input[type=text]:hover,textarea:hover{border:1px solid #1155AA!important;}
|
||||
input::-webkit-calendar-picker-indicator{color:#1155AA;background:none;}
|
||||
/**容器*/
|
||||
.taoyao{text-align:center;}
|
||||
/**会议*/
|
||||
.taoyao .handler a{cursor:pointer;}
|
||||
.taoyao > .handler{font-size:2rem;padding:1rem 0;width:100%;}
|
||||
.taoyao .list{width:90vw;margin:auto;}
|
||||
.taoyao .meeting{float:left;overflow:hidden;position:relative;width:calc(25% - 2rem);border:1rem solid #fff;}
|
||||
.taoyao .me,.taoyao .meeting:hover{border-color:#060;}
|
||||
.taoyao .meeting > .video{height:15vw;}
|
||||
.taoyao .meeting > .video video{width:100%;height:100%;}
|
||||
.taoyao .meeting .handler{position:absolute;bottom:0rem;text-align:center;width:100%;background:rgba(0,0,0,0.2);padding:0.2rem 0;}
|
||||
.taoyao .meeting .handler a{color:#fff;}
|
||||
.taoyao .meeting .handler a:hover{color:#060!important;}
|
||||
.taoyao .meeting .handler a.expel:hover{color:#C00!important;}
|
||||
.taoyao .meeting .handler a.record.active{color:#C00;}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,128 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>会议</title>
|
||||
<link rel="stylesheet" type="text/css" href="./css/font.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="./css/style.css" />
|
||||
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
|
||||
<script type="text/javascript" src="./javascript/taoyao.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="taoyao" id="app">
|
||||
<div class="handler">
|
||||
<a class="create icon-make-group" title="创建会议" @click="create"></a>
|
||||
<a class="invite icon-address-book" title="邀请会议" @click="invite"></a>
|
||||
<a class="enter icon-enter" title="进入会议" @click="enter"></a>
|
||||
<a class="leave icon-exit" title="离开会议" @click="leave"></a>
|
||||
<a class="close icon-switch" title="关闭会议" @click="close"></a>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div class="meeting me">
|
||||
<div class="video">
|
||||
<video id="local"></video>
|
||||
</div>
|
||||
<div class="handler">
|
||||
<a class="audio icon-volume-medium" title="音频状态" @click="audioSelf"></a>
|
||||
<a class="video icon-play2" title="视频状态" @click="videoSelf"></a>
|
||||
<a class="record icon-radio-checked" title="录制视频" @click="recordSelf"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="meeting" v-for="client in this.remoteClient" :key="client.sn">
|
||||
<div class="video">
|
||||
<video v-bind:id="client.sn"></video>
|
||||
</div>
|
||||
<div class="handler">
|
||||
<a class="audio" title="音频状态" v-bind:class="client.audioStatus?'icon-volume-medium':'icon-volume-mute2'" @click="audio(client.sn)"></a>
|
||||
<a class="video" title="视频状态" v-bind:class="client.videoStatus?'icon-play2':'icon-stop'" @click="video(client.sn)"></a>
|
||||
<a class="record icon-radio-checked" title="录制视频" v-bind:class="client.recordStatus?'active':''" @click="record(client.sn)"></a>
|
||||
<a class="expel icon-cancel-circle" title="踢出会议" @click="expel(client.sn)"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
data: {
|
||||
taoyao: null,
|
||||
remoteClient: [],
|
||||
meetingId: null
|
||||
},
|
||||
mounted() {
|
||||
// 设置帐号
|
||||
let sn = prompt('你的账号', signalConfig.sn);
|
||||
signalConfig.sn = sn;
|
||||
// 加载桃夭
|
||||
let self = this;
|
||||
this.taoyao = new Taoyao('local');
|
||||
this.remoteClient = this.taoyao.remoteClient;
|
||||
// 打开信令通道
|
||||
this.taoyao
|
||||
.buildChannel(self.callback)
|
||||
.then(e => console.debug('信令通道连接成功'));
|
||||
},
|
||||
beforeDestroy() {
|
||||
},
|
||||
methods: {
|
||||
// 信令回调:true表示已经处理;false表示没有处理;
|
||||
callback: function(data) {
|
||||
let self = this;
|
||||
switch(data.header.pid) {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// 创建会议
|
||||
create: function(event) {
|
||||
let self = this;
|
||||
this.taoyao.meetingCreate(data => {
|
||||
self.taoyao.meetingEnter(data.body.id);
|
||||
return true;
|
||||
});
|
||||
},
|
||||
// 会议邀请
|
||||
invite: function(sn) {
|
||||
},
|
||||
// 进入会议
|
||||
enter: function(sn) {
|
||||
let id = prompt('房间标识');
|
||||
let password = prompt('房间密码');
|
||||
if(id) {
|
||||
this.taoyao.meetingEnter(id);
|
||||
}
|
||||
},
|
||||
// 离开会议
|
||||
leave: function(sn) {
|
||||
},
|
||||
// 关闭会议
|
||||
close: function(sn) {
|
||||
},
|
||||
// 控制音频
|
||||
audio: function(sn) {
|
||||
this.client(sn).audio = !this.client(sn).audio;
|
||||
},
|
||||
// 控制视频
|
||||
video: function(sn) {
|
||||
this.client(sn).video = !this.client(sn).video;
|
||||
},
|
||||
// 录制视频
|
||||
record: function(sn) {
|
||||
this.client(sn).record = !this.client(sn).record;
|
||||
},
|
||||
// 踢出会议
|
||||
expel: function(sn) {
|
||||
},
|
||||
// 控制音频
|
||||
audioSelf: function(sn) {
|
||||
},
|
||||
// 控制视频
|
||||
videoSelf: function(sn) {
|
||||
},
|
||||
// 录制视频
|
||||
recordSelf: function(sn) {
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user