[*] 日常优化

This commit is contained in:
acgist
2023-07-10 08:55:38 +08:00
parent ff74d8330f
commit 5811976de8
9 changed files with 158 additions and 79 deletions

View File

@@ -629,13 +629,17 @@ openssl pkcs12 -export -clcerts -in server.crt -inkey server.key -out server.p12
``` ```
# 安装路径 # 安装路径
--prefix=/data/dev/ffmpeg/build --prefix=/usr/local
--prefix=/usr/local/ffmpeg
# 执行文件路径 # 执行文件路径
--bindir=/data/dev/ffmpeg/bin --bindir=/usr/local/bin
--bindir=/usr/local/ffmpeg/bin
# 库文件路径 # 库文件路径
--libdir=/usr/local/lib --libdir=/usr/local/lib
--libdir=/usr/local/ffmpeg/lib
# 头文件路径 # 头文件路径
--includedir=/usr/local/include --includedir=/usr/local/include
--includedir=/usr/local/ffmpeg/include
``` ```
## 清理源码 ## 清理源码

View File

@@ -277,6 +277,7 @@ public class SessionClient extends Client {
if(sdp == null || sdpMid == null || sdpMLineIndex == null) { if(sdp == null || sdpMid == null || sdpMLineIndex == null) {
Log.w(SessionClient.class.getSimpleName(), "无效媒体协商:" + body); Log.w(SessionClient.class.getSimpleName(), "无效媒体协商:" + body);
} else { } else {
// TODO验证是否可能为空PC
this.peerConnection.addIceCandidate(new IceCandidate(sdpMid, sdpMLineIndex, sdp)); this.peerConnection.addIceCandidate(new IceCandidate(sdpMid, sdpMLineIndex, sdp));
} }
} }

View File

@@ -5,7 +5,7 @@ const os = require("os");
* 一半配置本机IP地址用于Mediasoup媒体协商时SDP地址信息。 * 一半配置本机IP地址用于Mediasoup媒体协商时SDP地址信息。
* 如果存在多网卡或者多子网时,需要配置信令地址重写和防火墙端口转发。 * 如果存在多网卡或者多子网时,需要配置信令地址重写和防火墙端口转发。
*/ */
defaultTaoyaoHost = "192.168.1.110"; const defaultTaoyaoHost = "192.168.1.110";
/** /**
* 配置 * 配置
@@ -13,25 +13,27 @@ defaultTaoyaoHost = "192.168.1.110";
module.exports = { module.exports = {
// 服务名称 // 服务名称
name: "taoyao-client-media", name: "taoyao-client-media",
// 服务配置 // 信令配置
signal: { signal: {
// 服务版本 // 信令版本
version: "1.0.0", version : "1.0.0",
// 终端标识 // 终端标识
clientId: "taoyao-client-media", clientId : "taoyao-client-media",
// 终端类型
clientType: "MEDIA",
// 终端名称 // 终端名称
name: "桃夭媒体服务", name : "桃夭媒体服务",
// 信令地址 // 信令地址
host: "127.0.0.1", host : "127.0.0.1",
// host: "192.168.1.100", // host : "192.168.1.100",
// 信令端口 // 信令端口
port: 8888, port : 8888,
// 信令协议 // 信令协议
scheme: "wss", scheme : "wss",
// 信令帐号 // 信令帐号
username: "taoyao", username : "taoyao",
// 信令密码 // 信令密码
password: "taoyao", password : "taoyao",
}, },
// 录像配置 // 录像配置
record: { record: {

View File

@@ -92,7 +92,7 @@ const signalChannel = {
if (me.heartbeatTimer) { if (me.heartbeatTimer) {
clearTimeout(me.heartbeatTimer); clearTimeout(me.heartbeatTimer);
} }
me.heartbeatTimer = setTimeout(async function () { me.heartbeatTimer = setTimeout(async () => {
if (me.connected()) { if (me.connected()) {
me.push( me.push(
protocol.buildMessage("client::heartbeat", { protocol.buildMessage("client::heartbeat", {
@@ -120,7 +120,7 @@ const signalChannel = {
* @param {*} address 信令地址 * @param {*} address 信令地址
* @param {*} reconnection 是否重连 * @param {*} reconnection 是否重连
* *
* @returns Promise * @returns Promise<WebSocket>
*/ */
async connect(address, reconnection = true) { async connect(address, reconnection = true) {
const me = this; const me = this;
@@ -134,13 +134,13 @@ const signalChannel = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
console.debug("连接信令通道", me.address); console.debug("连接信令通道", me.address);
me.channel = new WebSocket(me.address, { rejectUnauthorized: false, handshakeTimeout: 5000 }); me.channel = new WebSocket(me.address, { rejectUnauthorized: false, handshakeTimeout: 5000 });
me.channel.on("open", async function () { me.channel.on("open", async () => {
console.info("打开信令通道", me.address); console.info("打开信令通道", me.address);
me.push( me.push(
protocol.buildMessage("client::register", { protocol.buildMessage("client::register", {
name : config.signal.name, name : config.signal.name,
clientId : config.signal.clientId, clientId : config.signal.clientId,
clientType: "MEDIA", clientType: config.signal.clientType,
username : config.signal.username, username : config.signal.username,
password : config.signal.password, password : config.signal.password,
// TODO电池信息 // TODO电池信息
@@ -153,7 +153,7 @@ const signalChannel = {
me.heartbeat(); me.heartbeat();
resolve(me.channel); resolve(me.channel);
}); });
me.channel.on("close", async function () { me.channel.on("close", async () => {
console.warn("信令通道关闭", me.address); console.warn("信令通道关闭", me.address);
me.taoyao.connect = false; me.taoyao.connect = false;
if(!me.connected()) { if(!me.connected()) {
@@ -164,11 +164,11 @@ const signalChannel = {
} }
// 不要失败回调 // 不要失败回调
}); });
me.channel.on("error", async function (e) { me.channel.on("error", async (e) => {
console.error("信令通道异常", me.address, e); console.error("信令通道异常", me.address, e);
// 不要失败回调 // 不要失败回调
}); });
me.channel.on("message", async function (data) { me.channel.on("message", async (data) => {
const content = data.toString(); const content = data.toString();
try { try {
console.debug("信令通道消息", content); console.debug("信令通道消息", content);
@@ -196,7 +196,7 @@ const signalChannel = {
clearTimeout(me.reconnectTimer); clearTimeout(me.reconnectTimer);
} }
// 定时重连 // 定时重连
me.reconnectTimer = setTimeout(function () { me.reconnectTimer = setTimeout(() => {
console.info("重连信令通道", me.address); console.info("重连信令通道", me.address);
me.connect(me.address, me.reconnection); me.connect(me.address, me.reconnection);
me.lockReconnect = false; me.lockReconnect = false;
@@ -227,9 +227,9 @@ const signalChannel = {
console.info("关闭信令通道", me.address); console.info("关闭信令通道", me.address);
clearTimeout(me.heartbeatTimer); clearTimeout(me.heartbeatTimer);
clearTimeout(me.reconnectTimer); clearTimeout(me.reconnectTimer);
me.reconnection = false; me.reconnection = false;
me.channel.close();
me.taoyao.connect = false; me.taoyao.connect = false;
me.channel.close();
}, },
}; };

View File

@@ -150,12 +150,6 @@ export default {
}; };
}, },
mounted() { mounted() {
console.info(`
中庭地白树栖鸦,冷露无声湿桂花。
今夜月明人尽望,不知秋思落谁家。
:: https://gitee.com/acgist/taoyao
`);
}, },
methods: { methods: {
async connectSignal() { async connectSignal() {

View File

@@ -1,8 +1,18 @@
/** /**
* 配置{ min: 8000, exact: 32000, ideal: 32000, max: 48000 } * 配置
*/ */
const config = {
// 信令配置
signal: {
// 信令版本
version : "1.0.0",
// 终端类型
clientType: "WEB",
}
}
/** /**
* 音频默认配置 * 音频默认配置
* 配置:{ min: 8000, exact: 32000, ideal: 32000, max: 48000 }
* *
* https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings * https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings
*/ */
@@ -31,6 +41,7 @@ const defaultAudioConfig = {
/** /**
* 视频默认配置 * 视频默认配置
* 配置:{ min: 8000, exact: 32000, ideal: 32000, max: 48000 }
* *
* https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings * https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings
*/ */
@@ -147,6 +158,7 @@ const defaultRTCPeerConnectionConfig = {
}; };
export { export {
config,
defaultAudioConfig, defaultAudioConfig,
defaultVideoConfig, defaultVideoConfig,
defaultShareScreenConfig, defaultShareScreenConfig,

View File

@@ -1,5 +1,6 @@
import * as mediasoupClient from "mediasoup-client"; import * as mediasoupClient from "mediasoup-client";
import { import {
config,
defaultAudioConfig, defaultAudioConfig,
defaultVideoConfig, defaultVideoConfig,
defaultShareScreenConfig, defaultShareScreenConfig,
@@ -9,7 +10,7 @@ import {
} from "./Config.js"; } from "./Config.js";
/** /**
* 信令 * 信令协议
*/ */
const protocol = { const protocol = {
// 当前索引 // 当前索引
@@ -48,7 +49,7 @@ const protocol = {
const me = this; const me = this;
const message = { const message = {
header: { header: {
v : v || "1.0.0", v : v || config.signal.version,
id : id || me.buildId(), id : id || me.buildId(),
signal: signal, signal: signal,
}, },
@@ -97,7 +98,7 @@ const signalChannel = {
if (me.heartbeatTimer) { if (me.heartbeatTimer) {
clearTimeout(me.heartbeatTimer); clearTimeout(me.heartbeatTimer);
} }
me.heartbeatTimer = setTimeout(async function () { me.heartbeatTimer = setTimeout(async () => {
if (me.connected()) { if (me.connected()) {
const battery = await navigator.getBattery(); const battery = await navigator.getBattery();
me.push( me.push(
@@ -125,7 +126,7 @@ const signalChannel = {
* @param {*} address 信令地址 * @param {*} address 信令地址
* @param {*} reconnection 是否重连 * @param {*} reconnection 是否重连
* *
* @returns Promise * @returns Promise<WebSocket>
*/ */
async connect(address, reconnection = true) { async connect(address, reconnection = true) {
const me = this; const me = this;
@@ -139,14 +140,14 @@ const signalChannel = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
console.debug("连接信令通道", me.address); console.debug("连接信令通道", me.address);
me.channel = new WebSocket(me.address); me.channel = new WebSocket(me.address);
me.channel.onopen = async function () { me.channel.onopen = async () => {
console.info("打开信令通道", me.address); console.info("打开信令通道", me.address);
const battery = await navigator.getBattery(); const battery = await navigator.getBattery();
me.push( me.push(
protocol.buildMessage("client::register", { protocol.buildMessage("client::register", {
name : me.taoyao.name, name : me.taoyao.name,
clientId : me.taoyao.clientId, clientId : me.taoyao.clientId,
clientType: "WEB", clientType: config.signal.clientType,
username : me.taoyao.username, username : me.taoyao.username,
password : me.taoyao.password, password : me.taoyao.password,
battery : battery.level * 100, battery : battery.level * 100,
@@ -158,7 +159,7 @@ const signalChannel = {
me.heartbeat(); me.heartbeat();
resolve(me.channel); resolve(me.channel);
}; };
me.channel.onclose = async function () { me.channel.onclose = async () => {
console.warn("信令通道关闭", me.channel); console.warn("信令通道关闭", me.channel);
me.taoyao.connect = false; me.taoyao.connect = false;
if(!me.connected()) { if(!me.connected()) {
@@ -170,11 +171,11 @@ const signalChannel = {
} }
// 不要失败回调 // 不要失败回调
}; };
me.channel.onerror = async function (e) { me.channel.onerror = async (e) => {
console.error("信令通道异常", me.channel, e); console.error("信令通道异常", me.channel, e);
// 不要失败回调 // 不要失败回调
}; };
me.channel.onmessage = async function (e) { me.channel.onmessage = async (e) => {
const content = e.data; const content = e.data;
try { try {
console.debug("信令通道消息", content); console.debug("信令通道消息", content);
@@ -202,7 +203,7 @@ const signalChannel = {
clearTimeout(me.reconnectTimer); clearTimeout(me.reconnectTimer);
} }
// 定时重连 // 定时重连
me.reconnectTimer = setTimeout(function () { me.reconnectTimer = setTimeout(() => {
console.info("重连信令通道", me.address); console.info("重连信令通道", me.address);
me.connect(me.address, me.reconnection); me.connect(me.address, me.reconnection);
me.lockReconnect = false; me.lockReconnect = false;
@@ -230,27 +231,27 @@ const signalChannel = {
*/ */
close() { close() {
const me = this; const me = this;
console.info("关闭信令通道", me.address);
clearTimeout(me.heartbeatTimer); clearTimeout(me.heartbeatTimer);
clearTimeout(me.reconnectTimer); clearTimeout(me.reconnectTimer);
me.reconnection = false; me.reconnection = false;
me.channel.close();
me.taoyao.connect = false; me.taoyao.connect = false;
me.channel.close();
}, },
}; };
/** /**
* 会话 * 视频会话
*/ */
class Session { class Session {
// 会话ID // 会话ID
id; id;
// 远程终端名称
name;
// 音量
volume = "100%";
// 是否关闭 // 是否关闭
closed; closed;
// 音量
volume;
// 远程终端名称
name;
// 远程终端ID // 远程终端ID
clientId; clientId;
// 会话ID // 会话ID
@@ -263,17 +264,21 @@ class Session {
videoEnabled; videoEnabled;
// 本地音频 // 本地音频
localAudioTrack; localAudioTrack;
// 本地音频是否可用(暂停关闭)
localAudioEnabled; localAudioEnabled;
// 本地视频 // 本地视频
localVideoTrack; localVideoTrack;
// 本地视频是否可用(暂停关闭)
localVideoEnabled; localVideoEnabled;
// 远程音频 // 远程音频
remoteAudioTrack; remoteAudioTrack;
// 远程音频是否可用(暂停关闭)
remoteAudioEnabled; remoteAudioEnabled;
// 远程视频 // 远程视频
remoteVideoTrack; remoteVideoTrack;
// 远程视频是否可用(暂停关闭)
remoteVideoEnabled; remoteVideoEnabled;
// PeerConnection // WebRTC PeerConnection
peerConnection; peerConnection;
constructor({ constructor({
@@ -283,67 +288,91 @@ class Session {
audioEnabled, audioEnabled,
videoEnabled videoEnabled
}) { }) {
this.id = sessionId; this.id = sessionId;
this.name = name; this.closed = false;
this.closed = false; this.volume = "100%";
this.clientId = clientId; this.name = name;
this.sessionId = sessionId; this.clientId = clientId;
this.sessionId = sessionId;
this.audioEnabled = audioEnabled; this.audioEnabled = audioEnabled;
this.videoEnabled = videoEnabled; this.videoEnabled = videoEnabled;
} }
/**
* 暂停本地媒体
*
* @param {*} type 媒体类型
*/
async pause(type) { async pause(type) {
if(type === 'audio' && this.localAudioTrack) { if(type === 'audio' && this.localAudioTrack) {
this.localAudioEnabled = false; this.localAudioEnabled = false;
this.localAudioTrack.enabled = false; this.localAudioTrack.enabled = false;
} }
if(type === 'video' && this.localVideoTrack) { if(type === 'video' && this.localVideoTrack) {
this.localVideoEnabled = false; this.localVideoEnabled = false;
this.localVideoTrack.enabled = false; this.localVideoTrack.enabled = false;
} }
} }
/**
* 恢复本地媒体
*
* @param {*} type 媒体类型
*/
async resume(type) { async resume(type) {
if(type === 'audio' && this.localAudioTrack) { if(type === 'audio' && this.localAudioTrack) {
this.localAudioEnabled = true; this.localAudioEnabled = true;
this.localAudioTrack.enabled = true; this.localAudioTrack.enabled = true;
} }
if(type === 'video' && this.localVideoTrack) { if(type === 'video' && this.localVideoTrack) {
this.localVideoEnabled = true; this.localVideoEnabled = true;
this.localVideoTrack.enabled = true; this.localVideoTrack.enabled = true;
} }
} }
/**
* 暂停远程媒体
*
* @param {*} type 媒体类型
*/
async pauseRemote(type) { async pauseRemote(type) {
if(type === 'audio') { if(type === 'audio' && this.remoteAudioTrack) {
this.remoteAudioEnabled = false; this.remoteAudioEnabled = false;
this.remoteAudioTrack.enabled = false; this.remoteAudioTrack.enabled = false;
} }
if(type === 'video') { if(type === 'video' && this.remoteVideoTrack) {
this.remoteVideoEnabled = false; this.remoteVideoEnabled = false;
this.remoteVideoTrack.enabled = false; this.remoteVideoTrack.enabled = false;
} }
} }
/**
* 恢复远程媒体
*
* @param {*} type 媒体类型
*/
async resumeRemote(type) { async resumeRemote(type) {
if(type === 'audio') { if(type === 'audio' && this.remoteAudioTrack) {
this.remoteAudioEnabled = true; this.remoteAudioEnabled = true;
this.remoteAudioTrack.enabled = true; this.remoteAudioTrack.enabled = true;
} }
if(type === 'video') { if(type === 'video' && this.remoteVideoTrack) {
this.remoteVideoEnabled = true; this.remoteVideoEnabled = true;
this.remoteVideoTrack.enabled = true; this.remoteVideoTrack.enabled = true;
} }
} }
/**
* 关闭视频会话
*/
async close() { async close() {
if(this.closed) { if(this.closed) {
return; return;
} }
console.debug("会话关闭", this.sessionId); console.debug("会话关闭", this.sessionId);
this.closed = true; this.closed = true;
this.localAudioEnabled = false; this.localAudioEnabled = false;
this.localVideoEnabled = false; this.localVideoEnabled = false;
this.remoteAudioEnabled = false; this.remoteAudioEnabled = false;
this.remoteVideoEnabled = false; this.remoteVideoEnabled = false;
if(this.localAudioTrack) { if(this.localAudioTrack) {
@@ -368,21 +397,39 @@ class Session {
} }
} }
async addIceCandidate(candidate) { /**
* 添加媒体协商
*
* @param {*} candidate 媒体协商
* @param {*} index 重试次数
*/
async addIceCandidate(candidate, index = 0) {
if(this.closed) { if(this.closed) {
return; return;
} }
if(!candidate || candidate.sdpMid === undefined || candidate.sdpMLineIndex === undefined && candidate.candidate === undefined) { if(index >= 8) {
console.debug("添加媒体协商次数超限", candidate, index);
return;
}
if(
!candidate ||
candidate.sdpMid === undefined ||
candidate.candidate === undefined ||
candidate.sdpMLineIndex === undefined
) {
console.debug("无效媒体协商", candidate);
return; return;
} }
if(this.peerConnection) { if(this.peerConnection) {
await this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); await this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
} else { } else {
setTimeout(() => this.addIceCandidate(candidate), 50); console.debug("延迟添加媒体协商", candidate, index);
setTimeout(() => this.addIceCandidate(candidate, ++index), 100);
} }
} }
};
} // TODO:continue
/** /**
* 远程终端 * 远程终端
@@ -2694,6 +2741,18 @@ class Taoyao extends RemoteClient {
}); });
} }
/**
* 关闭媒体轨道
*
* @param {*} mediaTrack 媒体轨道
*/
closeMediaTrack(mediaTrack) {
if(!mediaTrack) {
return;
}
mediaTrack.stop();
}
/** /**
* 关闭视频房间媒体 * 关闭视频房间媒体
*/ */

View File

@@ -1,9 +1,16 @@
import App from "./App.vue"; import App from "./App.vue";
import ElementPlus from "element-plus";
import { createApp } from "vue"; import { createApp } from "vue";
import ElementPlus from "element-plus";
import "./assets/main.css"; import "./assets/main.css";
import "element-plus/dist/index.css"; import "element-plus/dist/index.css";
console.info(`
中庭地白树栖鸦,冷露无声湿桂花。
今夜月明人尽望,不知秋思落谁家。
:: https://gitee.com/acgist/taoyao
`);
const app = createApp(App); const app = createApp(App);
app.use(ElementPlus); app.use(ElementPlus);
app.mount("#app"); app.mount("#app");

View File

@@ -1,6 +1,6 @@
import fs from "node:fs"; import fs from "node:fs";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import { fileURLToPath, URL } from "node:url"; import { fileURLToPath, URL } from "node:url";
export default defineConfig({ export default defineConfig({
@@ -9,8 +9,8 @@ export default defineConfig({
port: 8443, port: 8443,
host: "0.0.0.0", host: "0.0.0.0",
https: { https: {
key : fs.readFileSync("src/certs/server.key"),
cert: fs.readFileSync("src/certs/server.crt"), cert: fs.readFileSync("src/certs/server.crt"),
key: fs.readFileSync("src/certs/server.key"),
}, },
}, },
resolve: { resolve: {