[*] 优化logger
This commit is contained in:
@@ -330,7 +330,9 @@ pm2 start | stop | restart taoyao-client
|
|||||||
firewall-cmd --zone=public --add-port=5173/tcp --permanent
|
firewall-cmd --zone=public --add-port=5173/tcp --permanent
|
||||||
# 信令服务
|
# 信令服务
|
||||||
firewall-cmd --zone=public --add-port=8888/tcp --permanent
|
firewall-cmd --zone=public --add-port=8888/tcp --permanent
|
||||||
# 媒体服务(数据):40000-49999
|
# 媒体服务(控制):建议关闭
|
||||||
|
firewall-cmd --zone=public --add-port=4443/tcp --permanent
|
||||||
|
# 媒体服务(数据)
|
||||||
firewall-cmd --zone=public --add-port=40000-49999/udp --permanent
|
firewall-cmd --zone=public --add-port=40000-49999/udp --permanent
|
||||||
|
|
||||||
firewall-cmd --reload
|
firewall-cmd --reload
|
||||||
@@ -339,6 +341,7 @@ firewall-cmd --list-ports
|
|||||||
# 删除端口
|
# 删除端口
|
||||||
#firewall-cmd --zone=public --remove-port=5173/udp --permanent
|
#firewall-cmd --zone=public --remove-port=5173/udp --permanent
|
||||||
#firewall-cmd --zone=public --remove-port=8888/udp --permanent
|
#firewall-cmd --zone=public --remove-port=8888/udp --permanent
|
||||||
|
#firewall-cmd --zone=public --remove-port=4443/tcp --permanent
|
||||||
#firewall-cmd --zone=public --remove-port=40000-49999/udp --permanent
|
#firewall-cmd --zone=public --remove-port=40000-49999/udp --permanent
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 297 KiB |
18
taoyao-client/src/static/index.html
Normal file
18
taoyao-client/src/static/index.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>桃夭</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="./css/style.css" />
|
||||||
|
<script type="text/javascript" src="./javascript/taoyao.js"></script>
|
||||||
|
<style type="text/css">
|
||||||
|
a{width:50%;height:100%;position:fixed;text-align:center;line-height:100%;font-size:4rem;display:flex;align-items:center;justify-content:center;}
|
||||||
|
a:last-child{left:50%;}
|
||||||
|
a:hover{color:#fff;background:#060;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a href="./live.html">直播</a>
|
||||||
|
<a href="./meeting.html">会议</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ws": "^8.12.0",
|
"ws": "^8.12.0",
|
||||||
"debug": "^4.3.1",
|
|
||||||
"mediasoup": "file:./mediasoup"
|
"mediasoup": "file:./mediasoup"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ module.exports = {
|
|||||||
listenIp: "0.0.0.0",
|
listenIp: "0.0.0.0",
|
||||||
listenPort: process.env.HTTPS_LISTEN_PORT || 4443,
|
listenPort: process.env.HTTPS_LISTEN_PORT || 4443,
|
||||||
// WebSocket连接密码
|
// WebSocket连接密码
|
||||||
username: 'taoyao',
|
username: "taoyao",
|
||||||
password: 'taoyao',
|
password: "taoyao",
|
||||||
tls: {
|
tls: {
|
||||||
cert: process.env.HTTPS_CERT_PUBLIC_KEY || `${__dirname}/certs/publicKey.pem`,
|
cert:
|
||||||
key: process.env.HTTPS_CERT_PRIVATE_KEY || `${__dirname}/certs/privateKey.pem`,
|
process.env.HTTPS_CERT_PUBLIC_KEY || `${__dirname}/certs/publicKey.pem`,
|
||||||
}
|
key:
|
||||||
|
process.env.HTTPS_CERT_PRIVATE_KEY ||
|
||||||
|
`${__dirname}/certs/privateKey.pem`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// Mediasoup
|
// Mediasoup
|
||||||
mediasoup: {
|
mediasoup: {
|
||||||
@@ -133,5 +136,20 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
maxSctpMessageSize: 262144,
|
maxSctpMessageSize: 262144,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
wellcome: `<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>桃夭媒体服务</title>
|
||||||
|
<style type="text/css">
|
||||||
|
p{text-align:center;}
|
||||||
|
a{text-decoration:none;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p><a href="https://gitee.com/acgist/taoyao">taoyao-media-server</a></p>
|
||||||
|
<p><a href="https://www.acgist.com">acgist</a></p>
|
||||||
|
</body>
|
||||||
|
</html>`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,44 +1,46 @@
|
|||||||
/**
|
/**
|
||||||
* 日志
|
* 日志
|
||||||
*/
|
*/
|
||||||
const debug = require("debug");
|
|
||||||
const config = require("./Config");
|
const config = require("./Config");
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
|
|
||||||
|
//
|
||||||
|
name = config.name;
|
||||||
|
|
||||||
constructor(prefix) {
|
constructor(prefix) {
|
||||||
const appName = config.name;
|
|
||||||
if (prefix) {
|
if (prefix) {
|
||||||
this._debug = debug(`${appName}:DEBUG:${prefix}`);
|
this.name = this.name + ':' + prefix;
|
||||||
this._info = debug(`${appName}:INFO:${prefix}`);
|
}
|
||||||
this._warn = debug(`${appName}:WARN:${prefix}`);
|
}
|
||||||
this._error = debug(`${appName}:ERROR:${prefix}`);
|
|
||||||
|
debug(...args) {
|
||||||
|
this.log(console.debug, 'DEBUG', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
info(...args) {
|
||||||
|
this.log(console.info, 'INFO', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(...args) {
|
||||||
|
this.log(console.warn, 'WARN', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(...args) {
|
||||||
|
this.log(console.error, 'ERROR', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(out, level, args) {
|
||||||
|
if(!args) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(args.length > 1 && args[0].length > 0) {
|
||||||
|
out(`${this.name}:${level}:${args[0]}`, ...args.slice(1));
|
||||||
|
} else if(args.length === 1 && args[0].length > 0) {
|
||||||
|
out(`${this.name}:${level}:${args[0]}`);
|
||||||
} else {
|
} else {
|
||||||
this._debug = debug(`${appName}:DEBUG`);
|
out("");
|
||||||
this._info = debug(`${appName}:INFO`);
|
|
||||||
this._warn = debug(`${appName}:WARN`);
|
|
||||||
this._error = debug(`${appName}:ERROR`);
|
|
||||||
}
|
}
|
||||||
this._debug.log = console.debug.bind(console);
|
|
||||||
this._info.log = console.info.bind(console);
|
|
||||||
this._warn.log = console.warn.bind(console);
|
|
||||||
this._error.log = console.error.bind(console);
|
|
||||||
}
|
|
||||||
|
|
||||||
get debug() {
|
|
||||||
return this._debug.log;
|
|
||||||
}
|
|
||||||
|
|
||||||
get info() {
|
|
||||||
return this._info.log;
|
|
||||||
}
|
|
||||||
|
|
||||||
get warn() {
|
|
||||||
return this._warn.log;
|
|
||||||
}
|
|
||||||
|
|
||||||
get error() {
|
|
||||||
return this._error.log;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const ws = require("ws");
|
const ws = require("ws");
|
||||||
const https = require("https");
|
const https = require("https");
|
||||||
const mediasoup = require("mediasoup");
|
// const mediasoup = require("mediasoup");
|
||||||
const config = require("./Config");
|
const config = require("./Config");
|
||||||
const Logger = require("./Logger");
|
const Logger = require("./Logger");
|
||||||
const Signal = require("./Signal");
|
const Signal = require("./Signal");
|
||||||
@@ -83,7 +83,7 @@ async function buildSignalServer() {
|
|||||||
logger.info("配置HTTPS服务...");
|
logger.info("配置HTTPS服务...");
|
||||||
httpsServer = https.createServer(tls, (request, response) => {
|
httpsServer = https.createServer(tls, (request, response) => {
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
response.end("taoyao media server");
|
response.end(config.wellcome);
|
||||||
});
|
});
|
||||||
logger.info("配置WebSocket服务...");
|
logger.info("配置WebSocket服务...");
|
||||||
webSocketServer = new ws.Server({ server: httpsServer });
|
webSocketServer = new ws.Server({ server: httpsServer });
|
||||||
@@ -125,7 +125,7 @@ async function buildSignalServer() {
|
|||||||
async function main() {
|
async function main() {
|
||||||
logger.info("开始启动:%s", config.name);
|
logger.info("开始启动:%s", config.name);
|
||||||
// 启动Mediasoup服务
|
// 启动Mediasoup服务
|
||||||
await buildMediasoupWorkers();
|
// await buildMediasoupWorkers();
|
||||||
// 启动服务
|
// 启动服务
|
||||||
await buildSignalServer();
|
await buildSignalServer();
|
||||||
logger.info("启动完成:%s", config.name);
|
logger.info("启动完成:%s", config.name);
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ class Signal {
|
|||||||
* @param {*} message 消息
|
* @param {*} message 消息
|
||||||
* @param {*} session websocket
|
* @param {*} session websocket
|
||||||
*/
|
*/
|
||||||
on(message, session) {}
|
on(message, session) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Signal;
|
module.exports = Signal;
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
/**
|
|
||||||
* 配置
|
|
||||||
*/
|
|
||||||
const os = require("os");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
// 系统名称
|
|
||||||
name: "taoyao-media-server",
|
|
||||||
// 交互式命令行
|
|
||||||
command: true,
|
|
||||||
// 信令服务
|
|
||||||
https: {
|
|
||||||
listenIp: "0.0.0.0",
|
|
||||||
listenPort: process.env.HTTPS_LISTEN_PORT || 4443,
|
|
||||||
// WebSocket连接密码
|
|
||||||
username: 'taoyao',
|
|
||||||
password: 'taoyao',
|
|
||||||
tls: {
|
|
||||||
cert: process.env.HTTPS_CERT_PUBLIC_KEY || `${__dirname}/certs/publicKey.pem`,
|
|
||||||
key: process.env.HTTPS_CERT_PRIVATE_KEY || `${__dirname}/certs/privateKey.pem`,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Mediasoup
|
|
||||||
mediasoup: {
|
|
||||||
// 按照CPU数量配置进程数量
|
|
||||||
numWorkers: Object.keys(os.cpus()).length,
|
|
||||||
// Worker:https://mediasoup.org/documentation/v3/mediasoup/api/#WorkerSettings
|
|
||||||
workerSettings: {
|
|
||||||
logLevel: "warn",
|
|
||||||
logTags: [
|
|
||||||
"bwe",
|
|
||||||
"ice",
|
|
||||||
"rtp",
|
|
||||||
"rtx",
|
|
||||||
"svc",
|
|
||||||
"dtls",
|
|
||||||
"info",
|
|
||||||
"sctp",
|
|
||||||
"srtp",
|
|
||||||
"rtcp",
|
|
||||||
"score",
|
|
||||||
"message",
|
|
||||||
"simulcast",
|
|
||||||
],
|
|
||||||
rtcMinPort: process.env.MEDIASOUP_MIN_PORT || 40000,
|
|
||||||
rtcMaxPort: process.env.MEDIASOUP_MAX_PORT || 49999,
|
|
||||||
},
|
|
||||||
// Router:https://mediasoup.org/documentation/v3/mediasoup/api/#RouterOptions
|
|
||||||
routerOptions: {
|
|
||||||
mediaCodecs: [
|
|
||||||
{
|
|
||||||
kind: "audio",
|
|
||||||
mimeType: "audio/opus",
|
|
||||||
clockRate: 48000,
|
|
||||||
channels: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "video",
|
|
||||||
mimeType: "video/VP8",
|
|
||||||
clockRate: 90000,
|
|
||||||
parameters: {
|
|
||||||
"x-google-start-bitrate": 1000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "video",
|
|
||||||
mimeType: "video/VP9",
|
|
||||||
clockRate: 90000,
|
|
||||||
parameters: {
|
|
||||||
"profile-id": 2,
|
|
||||||
"x-google-start-bitrate": 1000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "video",
|
|
||||||
mimeType: "video/h264",
|
|
||||||
clockRate: 90000,
|
|
||||||
parameters: {
|
|
||||||
"packetization-mode": 1,
|
|
||||||
"profile-level-id": "4d0032",
|
|
||||||
"level-asymmetry-allowed": 1,
|
|
||||||
"x-google-start-bitrate": 1000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "video",
|
|
||||||
mimeType: "video/h264",
|
|
||||||
clockRate: 90000,
|
|
||||||
parameters: {
|
|
||||||
"packetization-mode": 1,
|
|
||||||
"profile-level-id": "42e01f",
|
|
||||||
"level-asymmetry-allowed": 1,
|
|
||||||
"x-google-start-bitrate": 1000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// WebRtcServer:https://mediasoup.org/documentation/v3/mediasoup/api/#WebRtcServerOptions
|
|
||||||
webRtcServerOptions: {
|
|
||||||
listenInfos: [
|
|
||||||
{
|
|
||||||
protocol: "udp",
|
|
||||||
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
|
|
||||||
announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
|
|
||||||
port: 44444,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
protocol: "tcp",
|
|
||||||
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
|
|
||||||
announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
|
|
||||||
port: 44444,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// WebRtcTransport:https://mediasoup.org/documentation/v3/mediasoup/api/#WebRtcTransportOptions
|
|
||||||
webRtcTransportOptions: {
|
|
||||||
listenIps: [
|
|
||||||
{
|
|
||||||
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
|
|
||||||
announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
initialAvailableOutgoingBitrate: 1000000,
|
|
||||||
minimumAvailableOutgoingBitrate: 600000,
|
|
||||||
maxSctpMessageSize: 262144,
|
|
||||||
maxIncomingBitrate: 1500000,
|
|
||||||
},
|
|
||||||
// PlainTransport:https://mediasoup.org/documentation/v3/mediasoup/api/#PlainTransportOptions
|
|
||||||
plainTransportOptions: {
|
|
||||||
listenIp: {
|
|
||||||
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
|
|
||||||
announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
|
|
||||||
},
|
|
||||||
maxSctpMessageSize: 262144,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
/**
|
|
||||||
* 服务
|
|
||||||
*/
|
|
||||||
const fs = require("fs");
|
|
||||||
const ws = require("ws");
|
|
||||||
const https = require("https");
|
|
||||||
const mediasoup = require("mediasoup");
|
|
||||||
const config = require("./Config");
|
|
||||||
const Logger = require("./Logger");
|
|
||||||
const Signal = require("./Signal");
|
|
||||||
const command = require("./Command");
|
|
||||||
|
|
||||||
// 日志
|
|
||||||
const logger = new Logger();
|
|
||||||
// 信令
|
|
||||||
const signal = new Signal();
|
|
||||||
// HTTPS server
|
|
||||||
let httpsServer;
|
|
||||||
// WebSocket server
|
|
||||||
let webSocketServer;
|
|
||||||
// Mediasoup Worker
|
|
||||||
const mediasoupWorkers = [];
|
|
||||||
|
|
||||||
process.title = config.name;
|
|
||||||
process.env.DEBUG = process.env.DEBUG || "*mediasoup* *INFO* *WARN* *ERROR*";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启动Mediasoup Worker
|
|
||||||
*/
|
|
||||||
async function buildMediasoupWorkers() {
|
|
||||||
const { numWorkers } = config.mediasoup;
|
|
||||||
logger.info("启动Mediasoup服务(%d Worker)...", numWorkers);
|
|
||||||
for (let i = 0; i < numWorkers; i++) {
|
|
||||||
// 新建Worker
|
|
||||||
const worker = await mediasoup.createWorker({
|
|
||||||
logLevel: config.mediasoup.workerSettings.logLevel,
|
|
||||||
logTags: config.mediasoup.workerSettings.logTags,
|
|
||||||
rtcMinPort: Number(config.mediasoup.workerSettings.rtcMinPort),
|
|
||||||
rtcMaxPort: Number(config.mediasoup.workerSettings.rtcMaxPort),
|
|
||||||
});
|
|
||||||
// 监听停止服务事件
|
|
||||||
worker.on("died", () => {
|
|
||||||
logger.error(
|
|
||||||
"Mediasoup Worker停止服务(两秒之后自动退出)... [PID:%d]",
|
|
||||||
worker.pid
|
|
||||||
);
|
|
||||||
setTimeout(() => process.exit(1), 2000);
|
|
||||||
});
|
|
||||||
// 加入队列
|
|
||||||
mediasoupWorkers.push(worker);
|
|
||||||
// 配置WebRTC服务
|
|
||||||
if (process.env.MEDIASOUP_USE_WEBRTC_SERVER !== "false") {
|
|
||||||
// 每个Worker端口不能相同
|
|
||||||
const portIncrement = mediasoupWorkers.length - 1;
|
|
||||||
const webRtcServerOptions = JSON.parse(JSON.stringify(config.mediasoup.webRtcServerOptions));
|
|
||||||
for (const listenInfo of webRtcServerOptions.listenInfos) {
|
|
||||||
listenInfo.port += portIncrement;
|
|
||||||
}
|
|
||||||
const webRtcServer = await worker.createWebRtcServer(webRtcServerOptions);
|
|
||||||
worker.appData.webRtcServer = webRtcServer;
|
|
||||||
}
|
|
||||||
// 定时记录使用日志
|
|
||||||
setInterval(async () => {
|
|
||||||
const usage = await worker.getResourceUsage();
|
|
||||||
logger.info(
|
|
||||||
"Mediasoup Worker使用情况 [pid:%d]: %o",
|
|
||||||
worker.pid,
|
|
||||||
usage
|
|
||||||
);
|
|
||||||
}, 120 * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启动信令服务
|
|
||||||
*/
|
|
||||||
async function buildSignalServer() {
|
|
||||||
const tls = {
|
|
||||||
cert: fs.readFileSync(config.https.tls.cert),
|
|
||||||
key: fs.readFileSync(config.https.tls.key),
|
|
||||||
};
|
|
||||||
logger.info("配置HTTPS服务...");
|
|
||||||
httpsServer = https.createServer(tls, (request, response) => {
|
|
||||||
response.writeHead(200);
|
|
||||||
response.end("taoyao media server");
|
|
||||||
});
|
|
||||||
logger.info("配置WebSocket服务...");
|
|
||||||
webSocketServer = new ws.Server({ server: httpsServer });
|
|
||||||
webSocketServer.on("connection", (session) => {
|
|
||||||
session.on("open", (message) => {
|
|
||||||
logger.info("打开信令通道: %s", message);
|
|
||||||
});
|
|
||||||
session.on("close", (code) => {
|
|
||||||
logger.info("关闭信令通道: %o", code);
|
|
||||||
});
|
|
||||||
session.on("error", (e) => {
|
|
||||||
logger.error("信令通道异常: %o", e);
|
|
||||||
});
|
|
||||||
session.on("message", (message) => {
|
|
||||||
logger.debug("收到信令消息: %s", message);
|
|
||||||
try {
|
|
||||||
signal.on(JSON.parse(message), session);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(
|
|
||||||
`处理信令消息异常:
|
|
||||||
%s
|
|
||||||
%o`,
|
|
||||||
message,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// 打开监听
|
|
||||||
httpsServer.listen(
|
|
||||||
Number(config.https.listenPort),
|
|
||||||
config.https.listenIp,
|
|
||||||
() => {
|
|
||||||
logger.info("信令服务启动完成");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
logger.info("开始启动:%s", config.name);
|
|
||||||
// 启动Mediasoup服务
|
|
||||||
await buildMediasoupWorkers();
|
|
||||||
// 启动服务
|
|
||||||
await buildSignalServer();
|
|
||||||
logger.info("启动完成:%s", config.name);
|
|
||||||
// 交互式命令行
|
|
||||||
if (config.command) {
|
|
||||||
await command();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* 信令
|
|
||||||
* 1. 终端媒体流向
|
|
||||||
* 2. 处理音频视频:降噪、水印等等
|
|
||||||
*/
|
|
||||||
class Signal {
|
|
||||||
|
|
||||||
// Mediasoup Worker列表
|
|
||||||
mediasoupWorkers = [];
|
|
||||||
// Mediasoup Worker下个索引
|
|
||||||
nextMediasoupWorkerIndex = 0;
|
|
||||||
|
|
||||||
constructor(mediasoupWorkers) {
|
|
||||||
this.mediasoupWorkers = mediasoupWorkers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理事件
|
|
||||||
*
|
|
||||||
* @param {*} message 消息
|
|
||||||
* @param {*} session websocket
|
|
||||||
*/
|
|
||||||
on(message, session) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Signal;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor;
|
|
||||||
|
|
||||||
public class MediaAggregateProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 并行媒体处理器
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaParallelProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor;
|
|
||||||
|
|
||||||
public class MediaRecordProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor.audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 降噪
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaDenoiseProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor.audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 混音
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaMixProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor.audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 变声器
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaWhineProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor.video;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 美颜
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaBeautyProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor.video;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AI识别
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaMarkHandler {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.media.processor.video;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 水印
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaWatermarkHandler {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.acgist.taoyao.mediasoup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mediasoup客户端
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
public class MediasoupClient {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.acgist.taoyao.mediasoup.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端媒体:producer、consumer
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
public class ClientStream {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.acgist.taoyao.mediasoup.router;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.acgist.taoyao.mediasoup.transport.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
public final class Router {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 传输通道列表
|
||||||
|
*/
|
||||||
|
private List<Transport> transportList;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.acgist.taoyao.mediasoup.transport;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.acgist.taoyao.mediasoup.client.ClientStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 传输通道
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
public final class Transport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生产者列表
|
||||||
|
*/
|
||||||
|
private List<ClientStream> producerList;
|
||||||
|
/**
|
||||||
|
* 消费者列表
|
||||||
|
*/
|
||||||
|
private List<ClientStream> consumerList;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>桃夭</title>
|
<title>桃夭信令服务</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./css/style.css" />
|
<style type="text/css">
|
||||||
<script type="text/javascript" src="./javascript/taoyao.js"></script>
|
p{text-align:center;}
|
||||||
<style type="text/css">
|
a{text-decoration:none;}
|
||||||
a{width:50%;height:100%;position:fixed;text-align:center;line-height:100%;font-size:4rem;display:flex;align-items:center;justify-content:center;}
|
</style>
|
||||||
a:last-child{left:50%;}
|
|
||||||
a:hover{color:#fff;background:#060;}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a href="./live.html">直播</a>
|
<p><a href="https://gitee.com/acgist/taoyao">taoyao-signal-server</a></p>
|
||||||
<a href="./meeting.html">会议</a>
|
<p><a href="https://www.acgist.com">acgist</a></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.acgist.taoyao.signal.listener;
|
package com.acgist.taoyao.signal.listener;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.signal.event.ApplicationEventAdapter;
|
import com.acgist.taoyao.signal.event.ApplicationEventAdapter;
|
||||||
import com.acgist.taoyao.signal.media.MediaRouterManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 媒体事件监听适配器
|
* 媒体事件监听适配器
|
||||||
@@ -14,7 +11,4 @@ import com.acgist.taoyao.signal.media.MediaRouterManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class MediaListenerAdapter<E extends ApplicationEventAdapter> extends ApplicationListenerAdapter<E> {
|
public abstract class MediaListenerAdapter<E extends ApplicationEventAdapter> extends ApplicationListenerAdapter<E> {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected MediaRouterManager mediaRouterManager;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.boot.annotation.Manager;
|
|
||||||
import com.acgist.taoyao.signal.media.processor.ProcessorChain;
|
|
||||||
import com.acgist.taoyao.signal.media.router.MediaRouter;
|
|
||||||
import com.acgist.taoyao.signal.media.router.MediaRouterHandler;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 媒体路由管理
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Manager
|
|
||||||
public class MediaRouterManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 路由集合
|
|
||||||
* ID=路由器
|
|
||||||
* ID=LiveId/MeetingId
|
|
||||||
*/
|
|
||||||
private Map<Long, MediaRouter> routers = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private ProcessorChain processorChain;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建路由
|
|
||||||
*
|
|
||||||
* @param id ID
|
|
||||||
*
|
|
||||||
* @return 路由
|
|
||||||
*/
|
|
||||||
public MediaRouter build(Long id) {
|
|
||||||
return this.routers.computeIfAbsent(id, key -> {
|
|
||||||
final MediaRouter router = new MediaRouterHandler();
|
|
||||||
router.build();
|
|
||||||
router.processorChain(this.processorChain);
|
|
||||||
log.debug("创建路由:{}-{}", id, router);
|
|
||||||
return router;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param id ID
|
|
||||||
*
|
|
||||||
* @return 路由
|
|
||||||
*/
|
|
||||||
public MediaRouter router(Long id) {
|
|
||||||
return this.routers.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭路由
|
|
||||||
*
|
|
||||||
* @param id ID
|
|
||||||
*/
|
|
||||||
public void close(Long id) {
|
|
||||||
final MediaRouter router = this.router(id);
|
|
||||||
if(router == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
router.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.processor;
|
|
||||||
|
|
||||||
public class MediaMixProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.processor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 媒体流处理器:混音、美颜等等
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public interface MediaProcessor {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.processor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 媒体流处理器责任链
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class ProcessorChain {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.router;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.signal.media.stream.MediaHandlerAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体发布者
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaPublisher extends MediaHandlerAdapter {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.router;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.signal.media.processor.ProcessorChain;
|
|
||||||
import com.acgist.taoyao.signal.media.stream.MediaStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 媒体流路由器
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public interface MediaRouter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始路由
|
|
||||||
*/
|
|
||||||
void build();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 媒体发布者
|
|
||||||
*/
|
|
||||||
MediaPublisher publisher();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 媒体订阅者
|
|
||||||
*/
|
|
||||||
MediaSubscriber subscriber();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param processorChain 媒体流处理器责任链
|
|
||||||
*/
|
|
||||||
void processorChain(ProcessorChain processorChain);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 发布者媒体流
|
|
||||||
*/
|
|
||||||
List<MediaStream> streamPublisher();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param sns 订阅者终端标识
|
|
||||||
*
|
|
||||||
* @return 订阅者媒体流
|
|
||||||
*/
|
|
||||||
List<MediaStream> streamSubscriber(String ... sns);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type 媒体类型
|
|
||||||
*
|
|
||||||
* @return 发布者媒体流
|
|
||||||
*/
|
|
||||||
List<MediaStream> streamPublisher(MediaStream.Type type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type 媒体类型
|
|
||||||
* @param sns 订阅者终端标识
|
|
||||||
*
|
|
||||||
* @return 发布者媒体流
|
|
||||||
*/
|
|
||||||
List<MediaStream> streamSubscriber(MediaStream.Type type, String ... sns);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭路由
|
|
||||||
*/
|
|
||||||
void close();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.router;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.signal.media.processor.ProcessorChain;
|
|
||||||
import com.acgist.taoyao.signal.media.stream.MediaStream;
|
|
||||||
import com.acgist.taoyao.signal.media.stream.MediaStream.Type;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 媒体流路由器处理器
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class MediaRouterHandler implements MediaRouter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 媒体流处理器责任链
|
|
||||||
*/
|
|
||||||
private ProcessorChain processorChain;
|
|
||||||
/**
|
|
||||||
* 发布者
|
|
||||||
*/
|
|
||||||
private MediaPublisher mediaPublisher;
|
|
||||||
/**
|
|
||||||
* 订阅者
|
|
||||||
*/
|
|
||||||
private MediaSubscriber mediaSubscriber;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void build() {
|
|
||||||
this.mediaPublisher = new MediaPublisher();
|
|
||||||
this.mediaSubscriber = new MediaSubscriber();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaPublisher publisher() {
|
|
||||||
return this.mediaPublisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaSubscriber subscriber() {
|
|
||||||
return this.mediaSubscriber;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void processorChain(ProcessorChain processorChain) {
|
|
||||||
this.processorChain = processorChain;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MediaStream> streamPublisher() {
|
|
||||||
return this.mediaPublisher.getStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MediaStream> streamSubscriber(String ... sns) {
|
|
||||||
return this.mediaSubscriber.getStreams().stream()
|
|
||||||
.filter(v -> ArrayUtils.contains(sns, v.subscriber()))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MediaStream> streamPublisher(Type type) {
|
|
||||||
return this.mediaPublisher.getStreams().stream()
|
|
||||||
.filter(v -> v.type() == type)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MediaStream> streamSubscriber(Type type, String... sns) {
|
|
||||||
return this.mediaSubscriber.getStreams().stream()
|
|
||||||
.filter(v -> v.type() == type)
|
|
||||||
.filter(v -> ArrayUtils.contains(sns, v.subscriber()))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
try {
|
|
||||||
this.mediaPublisher.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("关闭发布者异常", e);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.mediaSubscriber.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("关闭订阅者异常", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.router;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.signal.media.stream.MediaHandlerAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体订阅者
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public class MediaSubscriber extends MediaHandlerAdapter {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.stream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体处理器
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public interface MediaHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开
|
|
||||||
* 注意:用于打开媒体流
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void open() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开
|
|
||||||
* 注意:用于管理媒体流
|
|
||||||
*
|
|
||||||
* @param stream 媒体流
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void open(MediaStream stream) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 暂停
|
|
||||||
* 注意:暂停时发送心跳防止通道关闭
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void pause() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 恢复
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void resume() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭
|
|
||||||
*
|
|
||||||
* @param id 终端媒体流ID
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void close() throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开
|
|
||||||
*
|
|
||||||
* @param type 媒体类型
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void open(MediaStream.Type type) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 暂停
|
|
||||||
* 注意:暂停时发送心跳防止通道关闭
|
|
||||||
*
|
|
||||||
* @param type 媒体类型
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void pause(MediaStream.Type type) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 恢复
|
|
||||||
*
|
|
||||||
* @param type 媒体类型
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void resume(MediaStream.Type type) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭
|
|
||||||
*
|
|
||||||
* @param type 媒体类型
|
|
||||||
*
|
|
||||||
* @param id 终端媒体流ID
|
|
||||||
*
|
|
||||||
* @throws IOException IO异常
|
|
||||||
*/
|
|
||||||
void close(MediaStream.Type type) throws IOException;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.stream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.boot.model.MessageCodeException;
|
|
||||||
import com.acgist.taoyao.signal.media.stream.MediaStream.Type;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体处理器适配器
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class MediaHandlerAdapter implements MediaHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 媒体流集合
|
|
||||||
*/
|
|
||||||
protected List<MediaStream> streams = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open() throws IOException {
|
|
||||||
throw MessageCodeException.of("禁止使用");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(MediaStream stream) throws IOException {
|
|
||||||
log.debug("打开媒体流:{}", stream);
|
|
||||||
this.streams.add(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pause() throws IOException {
|
|
||||||
this.streams.forEach(v -> {
|
|
||||||
try {
|
|
||||||
v.pause();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("暂停媒体流异常:{}", v, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resume() throws IOException {
|
|
||||||
this.streams.forEach(v -> {
|
|
||||||
try {
|
|
||||||
v.resume();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("恢复媒体流异常:{}", v, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
this.streams.forEach(v -> {
|
|
||||||
try {
|
|
||||||
v.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("关闭媒体流异常:{}", v, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(Type type) throws IOException {
|
|
||||||
throw MessageCodeException.of("禁止使用");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pause(Type type) throws IOException {
|
|
||||||
this.streams.stream().filter(v -> v.type() == type).forEach(v -> {
|
|
||||||
try {
|
|
||||||
v.pause();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("暂停媒体流异常:{}", v, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resume(Type type) throws IOException {
|
|
||||||
this.streams.stream().filter(v -> v.type() == type).forEach(v -> {
|
|
||||||
try {
|
|
||||||
v.resume();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("恢复媒体流异常:{}", v, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close(Type type) throws IOException {
|
|
||||||
this.streams.stream().filter(v -> v.type() == type).forEach(v -> {
|
|
||||||
try {
|
|
||||||
v.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("关闭媒体流异常:{}", v, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体流
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public interface MediaStream extends MediaHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体类型
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public enum Type {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 混合:音视频
|
|
||||||
*/
|
|
||||||
MIX,
|
|
||||||
/**
|
|
||||||
* 音频
|
|
||||||
*/
|
|
||||||
AUDIO,
|
|
||||||
/**
|
|
||||||
* 视频
|
|
||||||
*/
|
|
||||||
VIDEO;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体流状态
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public enum Status {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 没有激活
|
|
||||||
*/
|
|
||||||
IDLE,
|
|
||||||
/**
|
|
||||||
* 已经激活
|
|
||||||
*/
|
|
||||||
BUSY,
|
|
||||||
/**
|
|
||||||
* 已经暂停
|
|
||||||
*/
|
|
||||||
PAUSE,
|
|
||||||
/**
|
|
||||||
* 已经关闭
|
|
||||||
*/
|
|
||||||
CLOSE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 终端媒体流ID
|
|
||||||
*/
|
|
||||||
String id();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 终端媒体流类型
|
|
||||||
*/
|
|
||||||
Type type();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 终端媒体流状态
|
|
||||||
*/
|
|
||||||
Status status();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 发布者
|
|
||||||
*/
|
|
||||||
String publisher();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 订阅者
|
|
||||||
*/
|
|
||||||
String subscriber();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package com.acgist.taoyao.signal.media.stream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.acgist.taoyao.boot.model.MessageCodeException;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.ToString;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端媒体流适配器
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@ToString(of = {"id", "type", "status", "publisher", "subscriber"})
|
|
||||||
public abstract class MediaStreamAdapter<T> implements MediaStream {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标识
|
|
||||||
*/
|
|
||||||
protected String id;
|
|
||||||
/**
|
|
||||||
* 类型
|
|
||||||
*/
|
|
||||||
protected Type type;
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
protected Status status;
|
|
||||||
/**
|
|
||||||
* 发布者
|
|
||||||
*/
|
|
||||||
private String publisher;
|
|
||||||
/**
|
|
||||||
* 订阅者
|
|
||||||
*/
|
|
||||||
private String subscriber;
|
|
||||||
/**
|
|
||||||
* 真实流
|
|
||||||
*/
|
|
||||||
protected T stream;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String id() {
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type type() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status status() {
|
|
||||||
return this.status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String publisher() {
|
|
||||||
return this.publisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String subscriber() {
|
|
||||||
return this.subscriber;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(MediaStream stream) throws IOException {
|
|
||||||
throw MessageCodeException.of("禁止套娃");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user