From 129c36ed8092c9041cae78101fdbce0266c58588 Mon Sep 17 00:00:00 2001
From: acgist <289547414@qq.com>
Date: Sat, 25 Feb 2023 13:31:57 +0800
Subject: [PATCH] =?UTF-8?q?[+]=20=E6=95=B4=E4=BD=93=E6=9E=B6=E6=9E=84?=
=?UTF-8?q?=E8=B0=83=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
生产者接入完成
媒体作为信令服务的终端注册
---
README.md | 2 -
docs/Deploy.md | 10 +-
taoyao-client-harmony/README.md | 3 +
taoyao-client-media/README.md | 47 +-
taoyao-client-media/mediasoup | 2 +-
taoyao-client-media/package.json | 1 -
taoyao-client-media/src/Config.js | 52 +-
taoyao-client-media/src/Server.js | 130 +---
taoyao-client-media/src/Signal.js | 722 +++++++++++++++---
taoyao-client-media/src/certs/privateKey.pem | 28 -
taoyao-client-media/src/certs/publicKey.pem | 22 -
taoyao-client-media/src/index.html | 15 -
taoyao-client-web/src/App.vue | 26 +-
taoyao-client-web/src/components/Config.js | 13 +-
.../src/components/RemoteClient.vue | 14 +-
.../src/components/SettingRoom.vue | 37 +-
.../src/components/SettingSignal.vue | 4 +-
taoyao-client-web/src/components/Taoyao.js | 203 +++--
taoyao-signal-server/pom.xml | 5 -
.../taoyao/boot/annotation/Listener.java | 22 +
.../acgist/taoyao/boot/config/Constant.java | 72 +-
.../boot/config/IpRewriteProperties.java | 30 +
.../boot/config/IpRewriteRuleProperties.java | 27 +
.../taoyao/boot/config/MediaProperties.java | 4 -
.../boot/config/MediaServerProperties.java | 39 -
.../taoyao/boot/config/ScriptProperties.java | 4 -
.../taoyao/boot/config/TaoyaoProperties.java | 2 -
.../configuration/BootAutoConfiguration.java | 51 +-
.../SpringDocAutoConfiguration.java | 35 +-
.../WebMvcConfigurerAutoConfiguration.java | 23 +-
.../com/acgist/taoyao/boot/model/Header.java | 2 +-
.../com/acgist/taoyao/boot/model/Message.java | 31 +-
.../acgist/taoyao/boot/service/IdService.java | 9 +-
.../acgist/taoyao/boot/service/IpService.java | 48 --
.../boot/service/impl/IdServiceImpl.java | 35 +-
.../boot/service/impl/IpServiceImpl.java | 145 ----
.../acgist/taoyao/boot/utils/ErrorUtils.java | 35 +-
.../acgist/taoyao/boot/utils/JSONUtils.java | 4 +-
.../acgist/taoyao/boot/utils/MapUtils.java | 106 +++
.../acgist/taoyao/boot/utils/NetUtils.java | 198 +++++
.../taoyao-boot/src/main/resources/banner.txt | 4 +-
.../src/main/resources/logback-spring.xml | 34 +-
.../taoyao/boot/utils/NetUtilsTest.java | 72 ++
taoyao-signal-server/taoyao-server/pom.xml | 4 +
.../TaoyaoAutoConfiguration.java | 23 +-
.../taoyao/controller/ConfigController.java | 16 +-
.../taoyao/controller/ControlController.java | 88 ++-
.../interceptor/SecurityInterceptor.java | 15 +-
.../taoyao/interceptor/SlowInterceptor.java | 11 +-
...ientListener.java => QnjrcmzListener.java} | 12 +-
.../taoyao/listener/ThyjxcfListener.java | 29 +
.../acgist/taoyao/main/TaoyaoApplication.java | 1 +
.../main/resources/META-INF/spring.factories | 2 +-
.../src/main/resources/application-dev.yml | 20 +-
.../src/main/resources/application-prd.yml | 2 +
.../src/main/resources/application.yml | 40 +-
.../acgist/taoyao/service/IdServiceTest.java | 1 +
.../acgist/taoyao/service/IpServiceTest.java | 77 --
.../acgist/taoyao/signal/MapBodyGetter.java | 103 ---
.../acgist/taoyao/signal/client/Client.java | 35 +-
.../taoyao/signal/client/ClientAdapter.java | 85 ++-
.../taoyao/signal/client/ClientManager.java | 74 +-
.../taoyao/signal/client/ClientStatus.java | 81 +-
.../taoyao/signal/client/ClientType.java | 62 +-
.../signal/client/socket/SocketClient.java | 27 +-
.../socket/SocketSignalMessageHandler.java | 4 +-
.../client/websocket/WebSocketClient.java | 9 +-
.../client/websocket/WebSocketSignal.java | 11 +-
.../WebSocketSignalConfigurator.java | 10 +-
.../MediaClientAutoConfiguration.java | 30 -
.../ScriptAutoConfiguration.java | 37 +-
.../SignalAutoConfiguration.java | 6 +-
.../SocketSignalAutoConfiguration.java | 7 +-
.../WebSocketSignalAutoConfiguration.java | 4 +-
.../signal/controller/ClientController.java | 31 +-
.../signal/controller/ProtocolController.java | 21 +-
.../signal/controller/RoomController.java | 16 +-
.../signal/event/ApplicationEventAdapter.java | 34 +-
.../event/{client => }/ClientCloseEvent.java | 5 +-
.../signal/event/ClientEventAdapter.java | 12 +-
.../event/MediaClientRegisterEvent.java | 18 +
.../signal/event/MediaEventAdapter.java | 34 -
.../signal/event/MediaProduceEvent.java | 30 +
.../taoyao/signal/event/RoomEventAdapter.java | 14 +-
.../taoyao/signal/media/MediaClient.java | 309 --------
.../signal/media/MediaClientManager.java | 71 --
.../com/acgist/taoyao/signal/media/Peer.java | 44 --
.../com/acgist/taoyao/signal/media/Room.java | 143 ----
.../acgist/taoyao/signal/media/Transport.java | 53 --
.../taoyao/signal/protocol/Protocol.java | 12 +-
.../signal/protocol/ProtocolAdapter.java | 31 +-
.../protocol/ProtocolClientAdapter.java | 23 +-
.../protocol/ProtocolControlAdapter.java | 47 ++
.../signal/protocol/ProtocolManager.java | 41 +-
.../signal/protocol/ProtocolMediaAdapter.java | 47 --
.../signal/protocol/ProtocolRoomAdapter.java | 57 +-
.../protocol/client/ClientAlarmProtocol.java | 54 ++
.../client/ClientBroadcastProtocol.java | 25 +-
.../protocol/client/ClientCloseProtocol.java | 23 +-
.../protocol/client/ClientConfigProtocol.java | 52 +-
.../client/ClientHeartbeatProtocol.java | 20 +-
.../protocol/client/ClientListProtocol.java | 31 +-
.../client/ClientOfflineProtocol.java | 9 -
.../protocol/client/ClientOnlineProtocol.java | 21 +-
.../client/ClientRegisterProtocol.java | 91 ++-
.../protocol/client/ClientStatusProtocol.java | 10 +-
.../client/ClientUnicastProtocol.java | 5 +-
.../protocol/client/ClientWakeupProtocol.java | 23 +-
.../protocol/control/ControlBellProtocol.java | 44 ++
.../control/ControlPhotographProtocol.java | 44 ++
.../protocol/control/ControlPtzProtocol.java | 68 ++
.../control/ControlRecordProtocol.java | 45 ++
.../MediaAudioActiveSpeakerProtocol.java | 19 +-
.../protocol/media/MediaConsumeProtocol.java | 83 ++
.../media/MediaIceRestartProtocol.java | 29 +
.../protocol/media/MediaListProtocol.java | 52 --
.../protocol/media/MediaProduceProtocol.java | 74 +-
.../media/MediaProducerScoreProtocol.java | 32 +-
.../protocol/media/MediaRebootProtocol.java | 53 --
.../protocol/media/MediaRecordProtocol.java | 5 -
.../protocol/media/MediaRegisterProtocol.java | 70 --
.../MediaRouterRtpCapabilitiesProtocol.java | 17 +-
.../protocol/media/MediaShutdownProtocol.java | 51 --
.../MediaTransportWebRtcConnectProtocol.java | 26 +-
.../MediaTransportWebRtcCreateProtocol.java | 75 +-
.../platform/PlatformErrorProtocol.java | 61 +-
.../platform/PlatformRebootProtocol.java | 15 +-
.../platform/PlatformScriptProtocol.java | 8 +-
.../platform/PlatformShutdownProtocol.java | 26 +-
.../protocol/room/RoomCloseProtocol.java | 16 +-
.../protocol/room/RoomCreateProtocol.java | 55 +-
.../protocol/room/RoomEnterProtocol.java | 31 +-
.../protocol/room/RoomExpelProtocol.java | 5 +
.../protocol/room/RoomInviteProtocol.java | 5 +
.../protocol/room/RoomLeaveProtocol.java | 5 +
.../protocol/room/RoomListProtocol.java | 9 +-
.../protocol/room/RoomStatusProtocol.java | 5 +
.../protocol/room/RoomSubscribeProtocol.java | 10 +
.../room/RoomUnsubscribeProtocol.java | 10 +
.../system/SystemDiskspaceProtocol.java | 10 +
.../protocol/system/SystemRebootProtocol.java | 15 +-
.../system/SystemShutdownProtocol.java | 15 +-
.../service/impl/SecurityServiceImpl.java | 14 +-
.../signal/terminal/media/ClientWrapper.java | 101 +++
.../signal/terminal/media/Consumer.java | 20 +
.../signal/terminal/media/DataConsumer.java | 5 +
.../signal/terminal/media/DataProducer.java | 9 +
.../taoyao/signal/terminal/media/Kind.java | 35 +
.../signal/terminal/media/Producer.java | 47 ++
.../taoyao/signal/terminal/media/Room.java | 182 +++++
.../{ => terminal}/media/RoomManager.java | 63 +-
.../{ => terminal}/media/RoomStatus.java | 6 +-
.../signal/terminal/media/Transport.java | 71 ++
.../taoyao/signal/terminal/package-info.java | 4 +
.../wrapper/WebrtcPropertiesWrapper.java | 79 --
...ot.autoconfigure.AutoConfiguration.imports | 1 -
156 files changed, 3659 insertions(+), 2817 deletions(-)
create mode 100644 taoyao-client-harmony/README.md
delete mode 100644 taoyao-client-media/src/certs/privateKey.pem
delete mode 100644 taoyao-client-media/src/certs/publicKey.pem
delete mode 100644 taoyao-client-media/src/index.html
create mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/annotation/Listener.java
create mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteProperties.java
create mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteRuleProperties.java
delete mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaServerProperties.java
delete mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IpService.java
delete mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IpServiceImpl.java
create mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/MapUtils.java
create mode 100644 taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/NetUtils.java
create mode 100644 taoyao-signal-server/taoyao-boot/src/test/java/com/acgist/taoyao/boot/utils/NetUtilsTest.java
rename taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/{HTTPClientListener.java => QnjrcmzListener.java} (59%)
create mode 100644 taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/ThyjxcfListener.java
delete mode 100644 taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IpServiceTest.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/MapBodyGetter.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/MediaClientAutoConfiguration.java
rename taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/{client => }/ClientCloseEvent.java (67%)
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaClientRegisterEvent.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaEventAdapter.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaProduceEvent.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClient.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClientManager.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Peer.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Room.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Transport.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolControlAdapter.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolMediaAdapter.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientAlarmProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlBellProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPhotographProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPtzProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlRecordProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaConsumeProtocol.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaListProtocol.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRebootProtocol.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRecordProtocol.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRegisterProtocol.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaShutdownProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/room/RoomSubscribeProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/room/RoomUnsubscribeProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/system/SystemDiskspaceProtocol.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/ClientWrapper.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/Consumer.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/DataConsumer.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/DataProducer.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/Kind.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/Producer.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/Room.java
rename taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/{ => terminal}/media/RoomManager.java (68%)
rename taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/{ => terminal}/media/RoomStatus.java (88%)
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/media/Transport.java
create mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/terminal/package-info.java
delete mode 100644 taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/wrapper/WebrtcPropertiesWrapper.java
diff --git a/README.md b/README.md
index 7d358e5..c87d680 100644
--- a/README.md
+++ b/README.md
@@ -25,8 +25,6 @@
|taoyao-client-harmony|鸿蒙终端|鸿蒙智能终端接入|
|taoyao-signal-server|信令服务|终端信令控制|
-> 注意:只有Web实现完成信令控制
-
## 模式
监控模式、直播模式、会议模式、屏幕共享模式
diff --git a/docs/Deploy.md b/docs/Deploy.md
index 2aa9653..5b66967 100644
--- a/docs/Deploy.md
+++ b/docs/Deploy.md
@@ -246,7 +246,7 @@ git checkout taoyao
cd ..
npm install
-# 配置服务
+# 配置服务:服务名称必须和配置终端标识一致否则不能执行重启和关闭信令
pm2 start npm --name "taoyao-client-media" -- run dev | prd
pm2 save
@@ -255,7 +255,7 @@ pm2 ecosystem
pm2 start | reload ecosystem.config.json
pm2 save
-# 管理服务
+# 管理服务:服务名称必须和配置终端标识一致否则不能执行重启和关闭信令
pm2 start | stop | restart taoyao-client-media
```
@@ -364,6 +364,9 @@ pm2 save
# 管理服务
pm2 start | stop | restart taoyao-client-web
+# 打包代码
+npm run build
+
# Nginx配置
vim /etc/nginx/taoyao-client-web.cnf
@@ -393,8 +396,6 @@ firewall-cmd --zone=public --add-port=8443/tcp --permanent
firewall-cmd --zone=public --add-port=8888/tcp --permanent
# 信令服务(Socket):没有启用不用添加规则
firewall-cmd --zone=public --add-port=9999/tcp --permanent
-# 媒体服务(控制):只暴露给信令服务
-firewall-cmd --zone=public --add-rich-rule="rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="9443" accept" --permanent
# 媒体服务(数据)
firewall-cmd --zone=public --add-port=40000-49999/udp --permanent
@@ -405,7 +406,6 @@ firewall-cmd --list-ports
#firewall-cmd --zone=public --remove-port=8443/tcp --permanent
#firewall-cmd --zone=public --remove-port=8888/tcp --permanent
#firewall-cmd --zone=public --remove-port=9999/tcp --permanent
-#firewall-cmd --zone=public --remove-rich-rule="rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="9443" accept" --permanent
#firewall-cmd --zone=public --remove-port=40000-49999/udp --permanent
```
diff --git a/taoyao-client-harmony/README.md b/taoyao-client-harmony/README.md
new file mode 100644
index 0000000..20089e5
--- /dev/null
+++ b/taoyao-client-harmony/README.md
@@ -0,0 +1,3 @@
+# 鸿蒙终端
+
+提供嵌入式开发能力
diff --git a/taoyao-client-media/README.md b/taoyao-client-media/README.md
index c519853..6022ec8 100644
--- a/taoyao-client-media/README.md
+++ b/taoyao-client-media/README.md
@@ -1,4 +1,4 @@
-# 媒体
+# 媒体终端
只要负责媒体处理,不要添加任何业务逻辑,所有业务逻辑都由[taoyao-signal-server](../taoyao-signal-server)处理。
@@ -16,50 +16,9 @@ make
make -C worker
```
-## 事件
+## 节点配置
-```
-mediasoup.observer.on("newworker", fn(worker));
-worker.on("died", fn(error));
-worker.observer.on("close", fn());
-worker.observer.on("newrouter", fn(router));
-worker.observer.on("newwebrtcserver", fn(router));
-router.on(“workerclose”, fn());
-router.observer.on(“close”, fn());
-router.observer.on(“newtransport”, fn(transport));
-router.observer.on(“newrtpobserver”, fn(rtpObserver));
-transport.on("trace", fn(trace));
-transport.on(“routerclose”, fn());
-transport.on(“listenserverclose”, fn());
-transport.on(“trace”, fn(trace));
-webRtcTransport.on(“icestatechange”, fn(iceState))
-webRtcTransport.on(“iceselectedtuplechange”, fn(iceSelectedTuple))
-webRtcTransport.on(“dtlsstatechange”, fn(dtlsState))
-webRtcTransport.on(“sctpstatechange”, fn(sctpState))
-plainTransport.on(“tuple”, fn(tuple))
-plainTransport.on(“rtcptuple”, fn(rtcpTuple))
-plainTransport.on(“sctpstatechange”, fn(sctpState))
-pipeTransport.on(“sctpstatechange”, fn(sctpState))
-directTransport.on(“rtcp”, fn(rtcpPacket))
-transport.observer.on(“close”, fn())
-transport.observer.on(“newproducer”, fn(producer))
-transport.observer.on(“newconsumer”, fn(consumer))
-transport.observer.on(“newdataproducer”, fn(dataProducer))
-transport.observer.on(“newdataconsumer”, fn(dataConsumer))
-transport.observer.on(“trace”, fn(trace))
-webRtcTransport.observer.on(“icestatechange”, fn(iceState))
-webRtcTransport.observer.on(“iceselectedtuplechange”, fn(iceSelectedTuple))
-webRtcTransport.observer.on(“dtlsstatechange”, fn(dtlsState))
-webRtcTransport.observer.on(“sctpstatechange”, fn(sctpState))
-plainTransport.observer.on(“tuple”, fn(tuple))
-plainTransport.observer.on(“rtcptuple”, fn(rtcpTuple))
-plainTransport.observer.on(“sctpstatechange”, fn(sctpState))
-pipeTransport.observer.on(“sctpstatechange”, fn(sctpState))
-```
-
-## 安全
-
-默认媒体服务只要暴露媒体`UDP`端口,信令接口不用暴露,所以使用简单鉴权。
+需要保证`src/Config.js`中的`clientId`和`ecosystem.config.json`中的`name`保持一致,否者重启和关闭信令无效。
## 动态调节码率
diff --git a/taoyao-client-media/mediasoup b/taoyao-client-media/mediasoup
index f02edc6..64851a8 160000
--- a/taoyao-client-media/mediasoup
+++ b/taoyao-client-media/mediasoup
@@ -1 +1 @@
-Subproject commit f02edc62e764b061917cbd215528db62e27fc9bd
+Subproject commit 64851a8a813dc59f1269ea92c444fa36cceaf961
diff --git a/taoyao-client-media/package.json b/taoyao-client-media/package.json
index 6a6159a..d9e8e2a 100644
--- a/taoyao-client-media/package.json
+++ b/taoyao-client-media/package.json
@@ -10,7 +10,6 @@
},
"dependencies": {
"ws": "^8.12.0",
- "moment": "^2.29.4",
"mediasoup": "file:./mediasoup"
}
}
diff --git a/taoyao-client-media/src/Config.js b/taoyao-client-media/src/Config.js
index 6b5bfb2..783c7c6 100644
--- a/taoyao-client-media/src/Config.js
+++ b/taoyao-client-media/src/Config.js
@@ -6,27 +6,28 @@ const os = require("os");
module.exports = {
// 服务名称
name: "taoyao-client-media",
- // 服务版本
- version: "1.0.0",
- // 欢迎页面
- welcome: `${__dirname}/index.html`,
- // 日志级别
- logLevel: "DEBUG",
- // 录像目录
- recordStoragePath: "/data/record",
- // 信令服务
- https: {
- // 信令服务地址端口
- listenIp: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
- listenPort: process.env.HTTPS_LISTEN_PORT || 9443,
- // 信令服务安全配置
+ // 保存目录
+ storagePath: "/data/storage",
+ // 图片目录
+ storageImagePath: "/data/storage/image",
+ // 视频目录
+ storageVideoPath: "/data/storage/video",
+ // 信令配置
+ signal: {
+ // 服务版本
+ version: "1.0.0",
+ // 终端标识
+ clientId: "taoyao-client-media",
+ // 地址
+ host: "127.0.0.1",
+ // 端口
+ port: 8888,
+ // 协议
+ scheme: "wss",
+ // 信令用户
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`,
- },
},
// 水印
watermark: {
@@ -42,8 +43,7 @@ module.exports = {
workerSize: Object.keys(os.cpus()).length,
// Worker:https://mediasoup.org/documentation/v3/mediasoup/api/#WorkerSettings
workerSettings: {
- // 级别:debug | warn | error | none
- logLevel: "warn",
+ // 记录标记
logTags: [
"bwe",
"ice",
@@ -59,6 +59,8 @@ module.exports = {
"message",
"simulcast",
],
+ // 级别:debug | warn | error | none
+ logLevel: "warn",
rtcMinPort: process.env.MEDIASOUP_MIN_PORT || 40000,
rtcMaxPort: process.env.MEDIASOUP_MAX_PORT || 49999,
},
@@ -120,14 +122,14 @@ module.exports = {
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
port: 44444,
// 公网地址
- announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
+ announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP || "192.168.1.110",
},
{
protocol: "tcp",
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
port: 44444,
// 公网地址
- announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
+ announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP || "192.168.1.110",
},
],
},
@@ -137,7 +139,7 @@ module.exports = {
{
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
// 公网地址
- announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
+ announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP || "192.168.1.110",
},
],
initialAvailableOutgoingBitrate: 1000000,
@@ -150,7 +152,7 @@ module.exports = {
listenIp: {
ip: process.env.MEDIASOUP_LISTEN_IP || "0.0.0.0",
// 公网地址
- announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP,
+ announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP || "192.168.1.110",
},
maxSctpMessageSize: 262144,
},
diff --git a/taoyao-client-media/src/Server.js b/taoyao-client-media/src/Server.js
index a8135cf..6966b18 100644
--- a/taoyao-client-media/src/Server.js
+++ b/taoyao-client-media/src/Server.js
@@ -1,49 +1,44 @@
#!/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 Signal = require("./Signal");
+const mediasoup = require("mediasoup");
+const { Signal, signalChannel } = require("./Signal");
// 线程名称
process.title = config.name;
+// 禁止校验无效证书
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
-// 无效信令终端列表
-const clients = [];
// Mediasoup Worker列表
const mediasoupWorkers = [];
-
-// HTTPS server
-let httpsServer;
-// WebSocket server
-let webSocketServer;
-// 信令
+// 信令服务
const signal = new Signal(mediasoupWorkers);
/**
- * 启动Mediasoup Worker
+ * 创建Mediasoup Worker列表
*/
async function buildMediasoupWorkers() {
+ // 可配置的事件
+ // mediasoup.observer.on("newworker", fn(worker));
const { workerSize } = config.mediasoup;
- console.info("启动Worker", workerSize);
+ console.info("创建Mediasoup Worker数量:", workerSize);
for (let index = 0; index < workerSize; index++) {
- // 新建Worker
const worker = await mediasoup.createWorker({
- logLevel: config.mediasoup.workerSettings.logLevel,
logTags: config.mediasoup.workerSettings.logTags,
+ logLevel: config.mediasoup.workerSettings.logLevel,
rtcMinPort: Number(config.mediasoup.workerSettings.rtcMinPort),
rtcMaxPort: Number(config.mediasoup.workerSettings.rtcMaxPort),
});
- // 监听Worker事件
- worker.on("died", () => {
- console.warn("Worker停止服务", worker.pid);
+ worker.on("died", (error) => {
+ console.warn("Mediasoup Worker停止服务:", worker.pid, error);
setTimeout(() => process.exit(1), 2000);
});
worker.observer.on("close", () => {
- console.warn("Worker关闭服务", worker.pid);
+ console.warn("Mediasoup Worker关闭服务:", worker.pid);
});
+ // 可配置的事件
+ // worker.observer.on("newrouter", fn(router));
+ // worker.observer.on("newwebrtcserver", fn(router));
// 配置WebRTC服务
const webRtcServerOptions = JSON.parse(
JSON.stringify(config.mediasoup.webRtcServerOptions)
@@ -53,101 +48,34 @@ async function buildMediasoupWorkers() {
}
const webRtcServer = await worker.createWebRtcServer(webRtcServerOptions);
worker.appData.webRtcServer = webRtcServer;
- // 加入Worker队列
mediasoupWorkers.push(worker);
}
}
/**
- * 启动信令服务
+ * 连接信令服务
*/
-async function buildSignalServer() {
- const tls = {
- cert: fs.readFileSync(config.https.tls.cert),
- key: fs.readFileSync(config.https.tls.key),
- };
- // 配置HTTPS Server
- httpsServer = https.createServer(tls, (request, response) => {
- response.writeHead(200);
- response.end(fs.readFileSync(config.welcome));
- });
- // 配置WebSocket Server
- webSocketServer = new ws.Server({ server: httpsServer });
- webSocketServer.on("connection", (session) => {
- console.info("打开信令通道", session._socket.remoteAddress);
- session.datetime = Date.now();
- session.authorize = false;
- clients.push(session);
- session.on("close", (code) => {
- console.info("关闭信令通道", session._socket.remoteAddress, code);
- });
- session.on("error", (error) => {
- console.error("信令通道异常", session._socket.remoteAddress, error);
- });
- session.on("message", (message) => {
- console.debug("处理信令消息", message.toString());
- try {
- signal.on(JSON.parse(message), session);
- } catch (error) {
- console.error("处理信令消息异常", message.toString(), error);
- }
- });
- });
- // 打开监听
- httpsServer.listen(
- Number(config.https.listenPort),
- config.https.listenIp,
- () => {
- console.info("信令服务启动完成");
+async function connectSignalServer() {
+ await signalChannel.connect(
+ `wss://${config.signal.host}:${config.signal.port}/websocket.signal`,
+ function (message) {
+ signal.on(message);
}
);
}
-/**
- * 定时任务
- */
-async function buildInterval() {
- // 定时打印使用情况
- setInterval(async () => {
- signal.usage();
- }, 300 * 1000);
- // 定时清理过期无效终端
- setInterval(() => {
- let failSize = 0;
- let silentSize = 0;
- let successSize = 0;
- const datetime = Date.now();
- for (let index = 0; index < clients.length; index++) {
- const session = clients[index];
- if (session.authorize) {
- clients.splice(index, 1);
- successSize++;
- index--;
- } else if (datetime - session.datetime >= 5000) {
- clients.splice(index, 1);
- session.close();
- failSize++;
- index--;
- } else {
- silentSize++;
- }
- }
- console.info("定时清理无效信令终端(无效|静默|成功|现存)", failSize, silentSize, successSize, clients.length);
- }, 60 * 1000);
-}
-
/**
* 启动方法
*/
async function main() {
- console.info("桃之夭夭,灼灼其华。")
- console.info("之子于归,宜其室家。")
- console.info("开始启动", config.name);
+ console.log(`
+ 桃之夭夭,灼灼其华。
+ 之子于归,宜其室家。
+ `);
+ console.info("开始启动:", config.name);
await buildMediasoupWorkers();
- await buildSignalServer();
- await buildInterval();
- console.info("启动完成", config.name);
+ await connectSignalServer();
+ console.info("启动完成:", config.name);
}
-// 启动服务
main();
diff --git a/taoyao-client-media/src/Signal.js b/taoyao-client-media/src/Signal.js
index 5923037..5ee5de5 100644
--- a/taoyao-client-media/src/Signal.js
+++ b/taoyao-client-media/src/Signal.js
@@ -1,50 +1,216 @@
const config = require("./Config");
+const process = require("child_process");
+const WebSocket = require("ws");
/**
* 信令协议
*/
const protocol = {
// 当前索引
- index: 100000,
- // 最小索引
- minIndex: 100000,
+ index: 0,
// 最大索引
- maxIndex: 999999,
+ maxIndex: 1000,
/**
* @returns 索引
*/
- buildId: function () {
- if (this.index++ >= this.maxIndex) {
- this.index = this.minIndex;
+ buildId() {
+ if (++this.index >= this.maxIndex) {
+ this.index = 0;
}
- return Date.now() + "" + this.index;
+ return Date.now() * 1000 + this.index;
},
/**
* 生成信令消息
*
* @param {*} signal 信令标识
- * @param {*} roomId 房间标识
* @param {*} body 信令消息
* @param {*} id ID
*
* @returns 信令消息
*/
- buildMessage: function (signal, roomId, body = {}, id) {
+ buildMessage(signal, body = {}, id) {
const message = {
header: {
- v: config.version,
+ v: config.signal.version,
id: id || this.buildId(),
signal: signal,
},
- body: {
- roomId,
- ...body,
- },
+ body: body,
};
return message;
},
};
+/**
+ * 信令通道
+ */
+const signalChannel = {
+ // 通道
+ channel: null,
+ // 地址
+ address: null,
+ // 回调
+ callback: null,
+ // 心跳时间
+ heartbeatTime: 30 * 1000,
+ // 心跳定时器
+ heartbeatTimer: null,
+ // 是否重连
+ reconnection: true,
+ // 重连定时器
+ reconnectTimer: null,
+ // 防止重复重连
+ lockReconnect: false,
+ // 当前重连时间
+ connectionTimeout: 5 * 1000,
+ // 最小重连时间
+ minReconnectionDelay: 5 * 1000,
+ // 最大重连时间
+ maxReconnectionDelay: 60 * 1000,
+ /**
+ * 心跳
+ */
+ heartbeat() {
+ const self = this;
+ if (self.heartbeatTimer) {
+ clearTimeout(self.heartbeatTimer);
+ }
+ self.heartbeatTimer = setTimeout(async function () {
+ if (self.channel && self.channel.readyState === WebSocket.OPEN) {
+ // TODO:信号强度、电池信息
+ self.push(
+ protocol.buildMessage("client::heartbeat", {
+ signal: 100,
+ battery: 100,
+ charging: true,
+ })
+ );
+ self.heartbeat();
+ } else {
+ console.warn("发送心跳失败:", self.address);
+ }
+ }, self.heartbeatTime);
+ },
+ /**
+ * 连接
+ *
+ * @param {*} address 地址
+ * @param {*} callback 回调
+ * @param {*} reconnection 是否重连
+ *
+ * @returns Promise
+ */
+ async connect(address, callback, reconnection = true) {
+ const self = this;
+ if (self.channel && self.channel.readyState === WebSocket.OPEN) {
+ return new Promise((resolve, reject) => {
+ resolve(self.channel);
+ });
+ }
+ self.address = address;
+ self.callback = callback;
+ self.reconnection = reconnection;
+ return new Promise((resolve, reject) => {
+ console.debug("连接信令通道:", self.address);
+ self.channel = new WebSocket(self.address, { handshakeTimeout: 5000 });
+ self.channel.on("open", async function () {
+ console.info("打开信令通道:", self.address);
+ // 注册终端
+ // TODO:信号强度、电池信息
+ self.push(
+ protocol.buildMessage("client::register", {
+ name: "桃夭媒体服务",
+ clientId: config.signal.clientId,
+ clientType: "MEDIA",
+ signal: 100,
+ battery: 100,
+ charging: true,
+ username: config.signal.username,
+ password: config.signal.password,
+ })
+ );
+ // 重置时间
+ self.connectionTimeout = self.minReconnectionDelay;
+ // 开始心跳
+ self.heartbeat();
+ // 成功回调
+ resolve(self.channel);
+ });
+ self.channel.on("close", async function () {
+ console.warn("信令通道关闭:", self.address);
+ if (self.reconnection) {
+ self.reconnect();
+ }
+ // 不要失败回调
+ });
+ self.channel.on("error", async function (e) {
+ console.error("信令通道异常:", self.address, e);
+ if (self.reconnection) {
+ self.reconnect();
+ }
+ // 不要失败回调
+ });
+ self.channel.on("message", async function (data) {
+ try {
+ const content = data.toString();
+ console.debug("信令通道消息:", content);
+ self.callback(JSON.parse(content));
+ } catch (error) {
+ console.error("处理信令消息异常:", content, error);
+ }
+ });
+ });
+ },
+ /**
+ * 重连
+ */
+ reconnect() {
+ const self = this;
+ if (
+ self.lockReconnect ||
+ (self.channel && self.channel.readyState === WebSocket.OPEN)
+ ) {
+ return;
+ }
+ self.lockReconnect = true;
+ if (self.reconnectTimer) {
+ clearTimeout(self.reconnectTimer);
+ }
+ // 定时重连
+ self.reconnectTimer = setTimeout(function () {
+ console.info("信令通道重连:", self.address);
+ self.connect(self.address, self.callback, self.reconnection);
+ self.lockReconnect = false;
+ }, self.connectionTimeout);
+ self.connectionTimeout = Math.min(
+ self.connectionTimeout + self.minReconnectionDelay,
+ self.maxReconnectionDelay
+ );
+ },
+ /**
+ * 推送消息
+ *
+ * @param {*} message 消息
+ */
+ push(message) {
+ try {
+ this.channel.send(JSON.stringify(message));
+ } catch (error) {
+ console.error("推送消息异常:", message, error);
+ }
+ },
+ /**
+ * 关闭通道
+ */
+ close() {
+ const self = this;
+ self.reconnection = false;
+ self.channel.close();
+ clearTimeout(self.heartbeatTimer);
+ clearTimeout(self.reconnectTimer);
+ },
+};
+
/**
* 房间
*/
@@ -63,6 +229,8 @@ class Room {
audioLevelObserver = null;
// 音频监控
activeSpeakerObserver = null;
+ // 消费者复制数量
+ consumerReplicas = 0;
// 通道
transports = new Map();
// 生产者
@@ -94,60 +262,83 @@ class Room {
this.handleActiveSpeakerObserver();
}
+ /**
+ * 声音监控
+ */
handleAudioLevelObserver() {
+ const self = this;
// 声音
- this.audioLevelObserver.on("volumes", (volumes) => {
+ self.audioLevelObserver.on("volumes", (volumes) => {
for (const value of volumes) {
const { producer, volume } = value;
- this.signal.push(
- protocol.buildMessage("audio::active::speaker", this.roomId, {
- peerId: producer.appData.peerId,
+ signalChannel.push(
+ protocol.buildMessage("media::audio::active::speaker", {
+ roomId: self.roomId,
+ clientId: producer.clientId,
volume: volume,
})
);
}
});
// 静音
- this.audioLevelObserver.on("silence", () => {
- this.signal.push(
- protocol.buildMessage("audio::active::speaker", this.roomId, {
- peerId: null,
+ self.audioLevelObserver.on("silence", () => {
+ signalChannel.push(
+ protocol.buildMessage("media::audio::active::speaker", {
+ roomId: self.roomId,
})
);
});
}
+ /**
+ * 说话监控
+ */
handleActiveSpeakerObserver() {
- this.activeSpeakerObserver.on("dominantspeaker", (dominantSpeaker) => {
- console.debug("dominantspeaker", dominantSpeaker.producer.id);
+ const self = this;
+ self.activeSpeakerObserver.on("dominantspeaker", (dominantSpeaker) => {
+ console.debug(
+ "dominantspeaker:",
+ dominantSpeaker.producer.id,
+ dominantSpeaker.producer.clientId
+ );
});
}
+ /**
+ * 使用情况
+ */
usage() {
- console.info("房间标识", this.roomId);
- console.info("房间通道数量", this.transports.size);
- console.info("房间生产者数量", this.producers.size);
- console.info("房间消费者数量", this.consumers.size);
- console.info("房间数据生产者数量", this.dataProducers.size);
- console.info("房间数据消费者数量", this.dataConsumers.size);
+ console.info("房间标识:", this.roomId);
+ console.info("房间通道数量:", this.transports.size);
+ console.info("房间生产者数量:", this.producers.size);
+ console.info("房间消费者数量:", this.consumers.size);
+ console.info("房间数据生产者数量:", this.dataProducers.size);
+ console.info("房间数据消费者数量:", this.dataConsumers.size);
}
+ /**
+ * 关闭资源
+ */
close() {
- this.close = true;
- if (this.mediasoupRouter) {
- this.mediasoupRouter.close();
+ const self = this;
+ if (self.close) {
+ return;
+ }
+ self.close = true;
+ if (self.mediasoupRouter) {
+ self.mediasoupRouter.close();
}
}
}
/**
- * 信令
+ * 信令服务
*/
class Signal {
// 房间列表
rooms = new Map();
- // 信令终端列表
- clients = [];
+ // 回调事件
+ callbackMapping = new Map();
// Worker列表
mediasoupWorkers = [];
// Worker索引
@@ -155,51 +346,55 @@ class Signal {
constructor(mediasoupWorkers) {
this.mediasoupWorkers = mediasoupWorkers;
+ // 定时打印使用情况
+ setInterval(async () => {
+ this.usage();
+ }, 300 * 1000);
}
/**
- * 处理事件
+ * 处理信令消息
*
* @param {*} message 消息
- * @param {*} session websocket
*/
- on(message, session) {
- // 授权验证
- if (!session.authorize) {
- if (
- message?.header?.signal === "media::register" &&
- message?.body?.username === config.https.username &&
- message?.body?.password === config.https.password
- ) {
- console.debug("授权成功", session._socket.remoteAddress);
- this.clients.push(session);
- session.authorize = true;
- message.code = "0000";
- message.message = "授权成功";
- message.body.username = undefined;
- message.body.password = undefined;
- } else {
- console.warn("授权失败", session._socket.remoteAddress);
- message.code = "3401";
- message.message = "授权失败";
+ on(message) {
+ // 请求回调
+ if (this.callbackMapping.has(message.header.id)) {
+ try {
+ this.callbackMapping.get(message.header.id)(message);
+ } finally {
+ this.callbackMapping.delete(message.header.id);
}
- this.push(message, session);
return;
}
- // 处理信令
const body = message.body;
switch (message.header.signal) {
+ case "client::reboot":
+ this.clientReboot(message, body);
+ break;
+ case "client::shutdown":
+ this.clientShutdown(message, body);
+ break;
+ case "media::ice::restart":
+ this.mediaIceRestart(message, body);
+ break;
+ case "media::consume":
+ this.mediaConsume(message, body);
+ break;
+ case "media::produce":
+ this.mediaProduce(message, body);
+ break;
case "media::router::rtp::capabilities":
- this.mediaRouterRtpCapabilities(session, message, body);
+ this.mediaRouterRtpCapabilities(message, body);
break;
case "media::transport::webrtc::connect":
- this.mediaTransportWebrtcConnect(session, message, body);
+ this.mediaTransportWebrtcConnect(message, body);
break;
case "media::transport::webrtc::create":
- this.mediaTransportWebrtcCreate(session, message, body);
+ this.mediaTransportWebrtcCreate(message, body);
break;
case "room::create":
- this.roomCreate(session, message, body);
+ this.roomCreate(message, body);
break;
}
}
@@ -208,23 +403,41 @@ class Signal {
* 通知信令
*
* @param {*} message 消息
- * @param {*} session 信令通道
*/
- push(message, session) {
- if (session) {
+ push(message) {
+ signalChannel.push(message);
+ }
+
+ /**
+ * 同步请求
+ *
+ * @param {*} message 消息
+ *
+ * @returns Promise
+ */
+ async request(message) {
+ const self = this;
+ return new Promise((resolve, reject) => {
+ let done = false;
+ // 注册回调
+ self.callbackMapping.set(message.header.id, (response) => {
+ resolve(response);
+ done = true;
+ });
+ // 发送消息
try {
- session.send(JSON.stringify(message));
+ self.channel.send(JSON.stringify(message));
} catch (error) {
- console.error(
- "通知信令失败",
- session._socket.remoteAddress,
- message,
- error
- );
+ console.error("请求消息异常:", message, error);
}
- } else {
- this.clients.forEach((session) => this.push(message, session));
- }
+ // 设置超时
+ setTimeout(() => {
+ if (!done) {
+ self.callbackMapping.delete(message.header.id);
+ reject("请求超时", message);
+ }
+ }, 5000);
+ });
}
/**
@@ -233,11 +446,11 @@ class Signal {
async usage() {
for (const worker of this.mediasoupWorkers) {
const usage = await worker.getResourceUsage();
- console.info("Worker使用情况", worker.pid, usage);
+ console.info("Worker使用情况:", worker.pid, usage);
}
- console.info("路由数量", this.mediasoupWorkers.length);
- console.info("房间数量", this.rooms.size);
- Array.from(this.rooms.values()).forEach(room => room.usage());
+ console.info("路由数量:", this.mediasoupWorkers.length);
+ console.info("房间数量:", this.rooms.size);
+ Array.from(this.rooms.values()).forEach((room) => room.usage());
}
/**
@@ -252,39 +465,286 @@ class Signal {
}
/**
- * 路由RTP能力信令
+ * 重启终端信令
*
- * @param {*} session 信令通道
* @param {*} message 消息
* @param {*} body 消息主体
*/
- mediaRouterRtpCapabilities(session, message, body) {
- const { roomId } = body;
- const room = this.rooms.get(roomId);
- message.body = room.mediasoupRouter.rtpCapabilities;
- this.push(message, session);
- }
-
- async mediaTransportWebrtcConnect(session, message, body) {
- const { roomId, transportId, dtlsParameters } = body;
- const room = this.rooms.get(roomId);
- const transport = room.transports.get(transportId);
- if (!transport) {
- throw new Error(`transport with id "${transportId}" not found`);
- }
- await transport.connect({ dtlsParameters });
- message.body = { roomId };
- this.push(message, session);
+ clientReboot(message, body) {
+ process.exec(
+ `pm2 restart ${config.signal.clientId}`,
+ function (error, stdout, stderr) {
+ console.info("重启媒体服务:", error, stdout, stderr);
+ }
+ );
+ // this.push(message);
}
/**
- * @param {*} session 信令通道
+ * 关闭终端信令
+ *
* @param {*} message 消息
* @param {*} body 消息主体
*/
- async mediaTransportWebrtcCreate(session, message, body) {
+ clientShutdown(message, body) {
+ process.exec(
+ `pm2 stop ${config.signal.clientId}`,
+ function (error, stdout, stderr) {
+ console.info("关闭媒体服务:", error, stdout, stderr);
+ }
+ );
+ // this.push(message);
+ }
+
+ /**
+ * 媒体重启ICE信令
+ *
+ * @param {*} message 消息
+ * @param {*} body 消息主体
+ */
+ async mediaIceRestart(message, body) {
+ const { roomId, transportId } = body;
+ const room = this.rooms.get(roomId);
+ const transport = room.transports.get(transportId);
+ const iceParameters = await transport.restartIce();
+ message.body.iceParameters = iceParameters;
+ this.push(message);
+ }
+
+ async mediaProduce(message, body) {
const self = this;
- const { roomId, forceTcp, producing, consuming, sctpCapabilities } = body;
+ const {
+ kind,
+ roomId,
+ clientId,
+ streamId,
+ appData,
+ transportId,
+ rtpParameters,
+ } = body;
+ const room = self.rooms.get(roomId);
+ const transport = room.transports.get(transportId);
+ const producer = await transport.produce({
+ kind,
+ appData,
+ rtpParameters,
+ // keyFrameRequestDelay: 5000
+ });
+ producer.clientId = clientId;
+ producer.streamId = streamId;
+ room.producers.set(producer.id, producer);
+ // 打分
+ producer.on("score", (score) => {
+ self.push(
+ protocol.buildMessage("media::producer::score", {
+ roomId: roomId,
+ producerId: producer.id,
+ score,
+ })
+ );
+ });
+
+ producer.on("videoorientationchange", (videoOrientation) => {
+ logger.debug(
+ 'producer "videoorientationchange" event [producerId:%s, videoOrientation:%o]',
+ producer.id,
+ videoOrientation
+ );
+ });
+
+ producer.on("trace", (trace) => {
+ logger.debug(
+ 'producer "trace" event [producerId:%s, trace.type:%s, trace:%o]',
+ producer.id,
+ trace.type,
+ trace
+ );
+ });
+ message.body = { kind: kind, producerId: producer.id };
+ this.push(message);
+ if (producer.kind === "audio") {
+ room.audioLevelObserver
+ .addProducer({ producerId: producer.id })
+ .catch(() => {});
+ room.activeSpeakerObserver
+ .addProducer({ producerId: producer.id })
+ .catch(() => {});
+ }
+ }
+
+ async mediaConsume(message, body) {
+ const { roomId, producerId, transportId, rtpCapabilities } = body;
+ const room = this.rooms.get(roomId);
+ const producer = room.producers.get(producerId);
+ const transport = room.transports.get(transportId);
+ if (
+ !room ||
+ !producer ||
+ !transport ||
+ !rtpCapabilities ||
+ !room.mediasoupRouter.canConsume({
+ producerId: producerId,
+ rtpCapabilities: rtpCapabilities,
+ })
+ ) {
+ console.warn(
+ "不能消费媒体:",
+ roomId,
+ producerId,
+ transportId,
+ rtpCapabilities
+ );
+ return;
+ }
+ const promises = [];
+ const consumerCount = 1 + room.consumerReplicas;
+ for (let i = 0; i < consumerCount; i++) {
+ promises.push(
+ (async () => {
+ let consumer;
+ try {
+ consumer = await transport.consume({
+ producerId: producerId,
+ rtpCapabilities: rtpCapabilities,
+ // 暂停
+ paused: true,
+ });
+ } catch (error) {
+ console.error(
+ "创建消费者异常:",
+ roomId,
+ producerId,
+ transportId,
+ rtpCapabilities,
+ error
+ );
+ return;
+ }
+ room.consumers.set(consumer.id, consumer);
+ consumer.on("transportclose", () => {
+ room.consumers.delete(consumer.id);
+ });
+ consumer.on("producerclose", () => {
+ room.consumers.delete(consumer.id);
+ this.push(
+ protocol.buildMessage("media::consumer::close", {
+ consumerId: consumer.id,
+ })
+ );
+ });
+ consumer.on("producerpause", () => {
+ this.push(
+ protocol.buildMessage("media::consumer::pause", {
+ consumerId: consumer.id,
+ })
+ );
+ });
+ consumer.on("producerresume", () => {
+ this.push(
+ protocol.buildMessage("media::consumer::resume", {
+ consumerId: consumer.id,
+ })
+ );
+ });
+ consumer.on("score", (score) => {
+ this.push(
+ protocol.buildMessage("media::consumer::score", {
+ consumerId: consumer.id,
+ score,
+ })
+ );
+ });
+ consumer.on("layerschange", (layers) => {
+ this.push(
+ protocol.buildMessage("media::consumer::layers::change", {
+ consumerId: consumer.id,
+ spatialLayer: layers ? layers.spatialLayer : null,
+ temporalLayer: layers ? layers.temporalLayer : null,
+ })
+ );
+ });
+ consumer.on("trace", (trace) => {
+ logger.debug(
+ 'consumer "trace" event [producerId:%s, trace.type:%s, trace:%o]',
+ consumer.id,
+ trace.type,
+ trace
+ );
+ });
+ // TODO:改为同步
+ this.push(protocol.buildMessage("media::consume", {
+ //await this.request("media::consume", {
+ kind: consumer.kind,
+ type: consumer.type,
+ roomId: roomId,
+ producerId: producerId,
+ consumerId: consumer.id,
+ rtpParameters: consumer.rtpParameters,
+ appData: producer.appData,
+ producerPaused: consumer.producerPaused,
+ }));
+ await consumer.resume();
+ this.push(
+ protocol.buildMessage("media::consumer::score", {
+ consumerId: consumer.id,
+ score: consumer.score,
+ })
+ );
+ })()
+ );
+ }
+
+ try {
+ await Promise.all(promises);
+ } catch (error) {
+ console.warn("_createConsumer() | failed:%o", error);
+ }
+ }
+
+ /**
+ * 路由RTP能力信令
+ *
+ * @param {*} message 消息
+ * @param {*} body 消息主体
+ */
+ mediaRouterRtpCapabilities(message, body) {
+ const { roomId } = body;
+ const room = this.rooms.get(roomId);
+ message.body.rtpCapabilities = room.mediasoupRouter.rtpCapabilities;
+ this.push(message);
+ }
+
+ /**
+ * 连接WebRTC通道信令
+ *
+ * @param {*} message 消息
+ * @param {*} body 消息主体
+ */
+ async mediaTransportWebrtcConnect(message, body) {
+ const { roomId, transportId, dtlsParameters } = body;
+ const room = this.rooms.get(roomId);
+ const transport = room.transports.get(transportId);
+ await transport.connect({ dtlsParameters });
+ message.body = { roomId: roomId, transportId: transport.id };
+ this.push(message);
+ }
+
+ /**
+ * 创建WebRTC通道信令
+ *
+ * @param {*} message 消息
+ * @param {*} body 消息主体
+ */
+ async mediaTransportWebrtcCreate(message, body) {
+ const self = this;
+ const {
+ roomId,
+ clientId,
+ forceTcp,
+ producing,
+ consuming,
+ sctpCapabilities,
+ } = body;
const webRtcTransportOptions = {
...config.mediasoup.webRtcTransportOptions,
appData: { producing, consuming },
@@ -300,29 +760,41 @@ class Signal {
...webRtcTransportOptions,
webRtcServer: room.webRtcServer,
});
+ transport.clientId = clientId;
+ transport.on("icestatechange", (iceState) => {
+ console.debug(
+ "WebRtcTransport icestatechange event:",
+ iceState,
+ transport.id
+ );
+ });
transport.on("dtlsstatechange", (dtlsState) => {
console.debug(
- 'WebRtcTransport dtlsstatechange event',
- dtlsState
+ "WebRtcTransport dtlsstatechange event:",
+ dtlsState,
+ transport.id
);
});
transport.on("sctpstatechange", (sctpState) => {
console.debug(
- 'WebRtcTransport sctpstatechange event',
- sctpState
+ "WebRtcTransport sctpstatechange event:",
+ sctpState,
+ transport.id
);
});
- // await transport.enableTraceEvent([ 'probation', 'bwe' ]);
await transport.enableTraceEvent(["bwe"]);
transport.on("trace", (trace) => {
- console.debug(
- 'transport trace event',
- trace,
- trace.type,
- transport.id,
- );
+ console.debug("transport trace event:", trace, trace.type, transport.id);
});
- // Store the WebRtcTransport into the protoo Peer data Object.
+ // 可配置的事件
+ // transport.on("routerclose", fn());
+ // transport.on("listenserverclose", fn());
+ // transport.observer.on("close", fn());
+ // transport.observer.on("newproducer", fn(producer));
+ // transport.observer.on("newconsumer", fn(consumer));
+ // transport.observer.on("newdataproducer", fn(dataProducer));
+ // transport.observer.on("newdataconsumer", fn(dataConsumer));
+ // transport.observer.on("trace", fn(trace));
room.transports.set(transport.id, transport);
message.body = {
transportId: transport.id,
@@ -331,10 +803,7 @@ class Signal {
dtlsParameters: transport.dtlsParameters,
sctpParameters: transport.sctpParameters,
};
- self.push(
- message,
- session
- );
+ self.push(message);
const { maxIncomingBitrate } = config.mediasoup.webRtcTransportOptions;
// If set, apply max incoming bitrate limit.
if (maxIncomingBitrate) {
@@ -347,28 +816,27 @@ class Signal {
/**
* 创建房间信令
*
- * @param {*} session 信令通道
* @param {*} message 消息
* @param {*} body 消息主体
*
* @returns 房间
*/
- async roomCreate(session, message, body) {
+ async roomCreate(message, body) {
const roomId = body.roomId;
let room = this.rooms.get(roomId);
if (room) {
- this.push(message, session);
+ this.push(message);
return room;
}
const mediasoupWorker = this.nextMediasoupWorker();
const { mediaCodecs } = config.mediasoup.routerOptions;
const mediasoupRouter = await mediasoupWorker.createRouter({ mediaCodecs });
- mediasoupRouter.on("workerclose", () => {
- // TODO:通知房间关闭
- });
mediasoupRouter.observer.on("close", () => {
// TODO:通知房间关闭
});
+ // 可配置的事件
+ // mediasoupRouter.on("workerclose", () => {});
+ // mediasoupRouter.observer.on("newtransport", fn(transport));
// TODO:下面两个监控改为配置启用
const audioLevelObserver = await mediasoupRouter.createAudioLevelObserver({
maxEntries: 1,
@@ -388,9 +856,9 @@ class Signal {
});
this.rooms.set(roomId, room);
console.info("创建房间", roomId);
- this.push(message, session);
+ this.push(message);
return room;
}
}
-module.exports = Signal;
+module.exports = { Signal, signalChannel };
diff --git a/taoyao-client-media/src/certs/privateKey.pem b/taoyao-client-media/src/certs/privateKey.pem
deleted file mode 100644
index b326ca4..0000000
--- a/taoyao-client-media/src/certs/privateKey.pem
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfmqQa2T/dGQIk
-cXYiRE47csmhtOEmDL9e1uncyFOYaDLopqma+z0OWV71HrT9m5lEAbK3HZy56CWL
-HNlFmnIztT84jTc11b3996cd6G1/5nffRj8D3FcSps9qVsRwAaEnKE6tGrCJhjWB
-+/qKIZDP3MmGOkf1guQMTPeZBOEzo+JmL2MOwaLk8dXbb4KHbrye31UnvoXoANs3
-1IbdAE3gRMDc40yBoVHeWOQ0h4c/wg1+rMhYPaUtgFkyqFr7zX1YvTa7BwZE4oVf
-l7f5k+FRWO8OKn++kHIlE9H3pjdaGKWIuYFRYCxNC9IfGazhPSfYHVgg0q/8UUKg
-jFqgbjIJAgMBAAECggEANs6ateGOjbUtyCfyQjgkiUOUu+PqQO+1s7KnYjqkgjyd
-5sh8i4zk3Y2RDyl5S3FoQzM2FK2liS2P3uKMNdugheMij5/mqqT4dkLZ72pGV9pj
-pZdwwjmi6PPBXCnpkPDuTw0HX2g/4SnmK/nEgjSejtKpnV9cIJHPD+5KRBCp6No+
-JLgVFdhCCMEmyzTOU9ASxgRuw0sxGmPsdg3ZewUmoDb94mGDDot500rCB+wnYHue
-ACegbaalrTWhY2DzYyNCfnR+F/mshIeqDjMVLsdPoj8MahdQbIoFcyNF+ts3pFVl
-MXUUuO3bhZtrSVIT+4r07u82XoXaBufyPzK6Mo1vNQKBgQC9uebhZ1jCyYgGro5o
-xuPuW+B5tHKGtlElJ0e7D/XjDvPWj3eGEkVCD5BH2L3qG9oqpKxX+hpmjc5ey21Q
-zcnAGG2gC9o+uGQxxjix8sbZ0HEr2J6EdRlKzIZ/N2EnTmU+K8WWWI4g2PEqsId9
-yt39EwU/D81bc0b5pcMGJTMk1wKBgQDXWxhywNFtPVreCeerXvvHLIQEtiZUMdhm
-8zWtqHNhdojYoeaFPifVcajBo41Jl9qSVCot8cmdhIzmPwbRk1Ob7MtBC8t7OtzT
-UmwT9nuxC5iSaGLJNBMtP3M0TzV7+qvvg+Az13kYtje9475LMEYdBLnCb4Mz1Y/R
-QOR0mhWkHwKBgBxoepajl9nKvVBq0K4Fodlt7mWqzD85i1rpz8bFtAaklYQ6BSaR
-E8e5dtwbKwyj0P3znE6sB0n1z8HH6f1gYuYdgkSloa8kgvQk/xY+COJSYK+1Br9E
-nV3i0/y2eRiel3BAs5w4dEec1DeVKSR/vM+JCo8PuasIzsbQuCvyY/8PAoGBALWH
-kT0xsZcej9j4inMXNq62pHYAQKDZ/2sQed/vTYsLSuEo39LTCOrPywum3LL7MQAF
-uCRQWr3PfKGc4ReJ04FtAgvLcHNos7niET5ml+8uMia/nP2zSrLqeCbQ2emu7H2S
-MUwhxm8BMk17iu2APKm7UQZHz1XDIF6oD6sGM1XLAoGALFpdCO486AbHYts2NHey
-vQ39u3WDlrgzIM6hs8BI0FEZIdtuNWa/wpZYPaiXUvwTPsfQA1eCqXXuQGzH0fFn
-g1M8RxsZ8XNjfQVpqSceZp+qFkTrR8zrbbQiZwBUm9WBdKfryMZHLhFraGLShFdM
-ONu5qg3tXgMfFpNcMyiGgeA=
------END PRIVATE KEY-----
\ No newline at end of file
diff --git a/taoyao-client-media/src/certs/publicKey.pem b/taoyao-client-media/src/certs/publicKey.pem
deleted file mode 100644
index 1099efa..0000000
--- a/taoyao-client-media/src/certs/publicKey.pem
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDnzCCAoegAwIBAgIJAIKYVI9RPbj+MA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV
-BAYTAkNOMQswCQYDVQQIEwJHRDELMAkGA1UEBxMCR1oxDzANBgNVBAoTBnRhb3lh
-bzEPMA0GA1UECxMGYWNnaXN0MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjIxMTA5
-MDEyMTM3WhcNMzIxMTA2MDEyMTM3WjBdMQswCQYDVQQGEwJDTjELMAkGA1UECBMC
-R0QxCzAJBgNVBAcTAkdaMQ8wDQYDVQQKEwZ0YW95YW8xDzANBgNVBAsTBmFjZ2lz
-dDESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAn5qkGtk/3RkCJHF2IkROO3LJobThJgy/Xtbp3MhTmGgy6Kapmvs9Dlle
-9R60/ZuZRAGytx2cueglixzZRZpyM7U/OI03NdW9/fenHehtf+Z330Y/A9xXEqbP
-albEcAGhJyhOrRqwiYY1gfv6iiGQz9zJhjpH9YLkDEz3mQThM6PiZi9jDsGi5PHV
-22+Ch268nt9VJ76F6ADbN9SG3QBN4ETA3ONMgaFR3ljkNIeHP8INfqzIWD2lLYBZ
-Mqha+819WL02uwcGROKFX5e3+ZPhUVjvDip/vpByJRPR96Y3WhiliLmBUWAsTQvS
-Hxms4T0n2B1YINKv/FFCoIxaoG4yCQIDAQABo2IwYDAdBgNVHQ4EFgQUPpT59FzS
-UUzHsxrKeGOQ/YeaqpswDgYDVR0PAQH/BAQDAgWgMBoGA1UdEQQTMBGCCWxvY2Fs
-aG9zdIcEfwAAATATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOC
-AQEAKsoYmBr9EmYev6sWVet8x+/YDZgXGZolZff3agPT+uFL/6mtyAqnFD/ncPOh
-n206l4RimSChVlEVx3pE4r5sBiLzPaX/dcCRoZNxEQMtjfYCk+4iRfkxhIvpqLzf
-ZsEGbJCh9JodG2xkYNViPF2AqR8OchEMRttYQa2dDkk2oDVMg0bmqgZgSD7vEjdk
-ovBhEIQ1Rhgv0yi9IT+kXYa+nTpc+/9m/GmmejtaFaVdpj+WuNqPf/WyzR+3JQWZ
-Y/O7ESF7tUcw0HSxNv/pk6Z13RQClUo6bPHzPF4JJw1tAIbkuyKZ6ZpErQePUEpk
-dVZPy7rDD3N/BI//0vBZmy0v1w==
------END CERTIFICATE-----
\ No newline at end of file
diff --git a/taoyao-client-media/src/index.html b/taoyao-client-media/src/index.html
deleted file mode 100644
index 0fbe2cb..0000000
--- a/taoyao-client-media/src/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
- 桃夭媒体服务
-
-
-
- taoyao-client-media
- acgist
-
-
\ No newline at end of file
diff --git a/taoyao-client-web/src/App.vue b/taoyao-client-web/src/App.vue
index 0aff1df..6b9bbe4 100644
--- a/taoyao-client-web/src/App.vue
+++ b/taoyao-client-web/src/App.vue
@@ -1,6 +1,12 @@
-
+
+
@@ -15,11 +21,14 @@ export default {
return {
taoyao: {},
roomVisible: false,
- signalVisible: true,
+ signalVisible: false,
};
},
mounted() {
- console.info("桃夭终端开始启动");
+ console.info(`
+ 中庭地白树栖鸦,冷露无声湿桂花。
+ 今夜月明人尽望,不知秋思落谁家。
+ `);
},
methods: {
buildSignal(config) {
@@ -28,19 +37,24 @@ export default {
self.signalVisible = false;
self.taoyao.buildSignal(self.callback);
},
- buildMedia(roomId) {
+ produceMedia() {
let self = this;
- self.taoyao.buildMedia(roomId);
+ self.taoyao.produceMedia();
},
/**
* 信令回调
*
* @param {*} data 消息
+ * @param {*} error 异常
*
* @return 是否继续执行
*/
- callback(data) {
+ callback(data, error) {
let self = this;
+ if(data.header.signal === "platform::error") {
+ console.error("发生异常:", data, error);
+ return false;
+ }
switch (data.header.signal) {
case "client::config":
self.roomVisible = true;
diff --git a/taoyao-client-web/src/components/Config.js b/taoyao-client-web/src/components/Config.js
index 8fa3ce0..437a453 100644
--- a/taoyao-client-web/src/components/Config.js
+++ b/taoyao-client-web/src/components/Config.js
@@ -3,20 +3,17 @@
*/
const protocol = {
// 当前索引
- index: 100000,
- // 最小索引
- minIndex: 100000,
+ index: 0,
// 最大索引
- maxIndex: 999999,
+ maxIndex: 1000,
/**
* @returns 索引
*/
buildId() {
- const self = this;
- if (self.index++ >= self.maxIndex) {
- self.index = self.minIndex;
+ if (++this.index >= this.maxIndex) {
+ this.index = 0;
}
- return Date.now() + "" + self.index;
+ return Date.now() * 1000 + this.index;
},
/**
* @param {*} signal 信令标识
diff --git a/taoyao-client-web/src/components/RemoteClient.vue b/taoyao-client-web/src/components/RemoteClient.vue
index 9bc74de..d8b1ce5 100644
--- a/taoyao-client-web/src/components/RemoteClient.vue
+++ b/taoyao-client-web/src/components/RemoteClient.vue
@@ -1,2 +1,14 @@
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/taoyao-client-web/src/components/SettingRoom.vue b/taoyao-client-web/src/components/SettingRoom.vue
index c0a34f9..000123b 100644
--- a/taoyao-client-web/src/components/SettingRoom.vue
+++ b/taoyao-client-web/src/components/SettingRoom.vue
@@ -16,7 +16,7 @@
@@ -26,10 +26,10 @@
@@ -62,6 +62,7 @@ export default {
return {
room: {},
rooms: [],
+ medias: [],
activeName: "enter",
localVisible: false,
};
@@ -77,28 +78,26 @@ export default {
},
methods: {
async init() {
- const response = await this.taoyao.request(
+ const roomResponse = await this.taoyao.request(
protocol.buildMessage("room::list")
);
- this.rooms = response.body;
+ this.rooms = roomResponse.body;
+ const mediaResponse = await this.taoyao.request(
+ protocol.buildMessage("client::list", {clientType:"MEDIA"})
+ );
+ this.medias = mediaResponse.body;
},
async enter() {
- await this.taoyao.request(
- protocol.buildMessage("room::enter", {
- ...this.room,
- })
- );
+ await this.taoyao.enter(this.room.roomId);
this.localVisible = false;
- this.$emit("buildMedia", this.room.roomId);
+ this.$emit("produceMedia");
},
async create() {
- const response = await this.taoyao.request(
- protocol.buildMessage("room::create", {
- ...this.room,
- })
- );
+ const room = await this.taoyao.create(this.room);
+ this.room.roomId = room.roomId;
+ await this.enter(room.roomId);
this.localVisible = false;
- this.$emit("buildMedia", response.body.roomId);
+ this.$emit("produceMedia");
},
},
};
diff --git a/taoyao-client-web/src/components/SettingSignal.vue b/taoyao-client-web/src/components/SettingSignal.vue
index 95688d3..6c3747b 100644
--- a/taoyao-client-web/src/components/SettingSignal.vue
+++ b/taoyao-client-web/src/components/SettingSignal.vue
@@ -42,11 +42,11 @@ export default {
username: "taoyao",
password: "taoyao",
},
- localVisible: true,
+ localVisible: false,
};
},
props: {
- signalVisible: true,
+ signalVisible: false,
},
watch: {
signalVisible() {
diff --git a/taoyao-client-web/src/components/Taoyao.js b/taoyao-client-web/src/components/Taoyao.js
index 9d5d317..81efbf7 100644
--- a/taoyao-client-web/src/components/Taoyao.js
+++ b/taoyao-client-web/src/components/Taoyao.js
@@ -51,6 +51,7 @@ const signalChannel = {
self.heartbeatTimer = setTimeout(async function () {
if (self.channel && self.channel.readyState === WebSocket.OPEN) {
const battery = await navigator.getBattery();
+ // TODO:信号强度
self.push(
protocol.buildMessage("client::heartbeat", {
signal: 100,
@@ -60,7 +61,7 @@ const signalChannel = {
);
self.heartbeat();
} else {
- console.warn("发送心跳失败", self.channel);
+ console.warn("发送心跳失败:", self.address);
}
}, self.heartbeatTime);
},
@@ -76,22 +77,26 @@ const signalChannel = {
async connect(address, callback, reconnection = true) {
const self = this;
if (self.channel && self.channel.readyState === WebSocket.OPEN) {
- return;
+ return new Promise((resolve, reject) => {
+ resolve(self.channel);
+ });
}
self.address = address;
self.callback = callback;
self.reconnection = reconnection;
return new Promise((resolve, reject) => {
- console.debug("连接信令通道", address);
- self.channel = new WebSocket(address);
- self.channel.onopen = async function (e) {
- console.debug("打开信令通道", e);
+ console.debug("连接信令通道:", self.address);
+ self.channel = new WebSocket(self.address);
+ self.channel.onopen = async function () {
+ console.debug("打开信令通道:", self.address);
// 注册终端
+ // TODO:信号强度
const battery = await navigator.getBattery();
self.push(
protocol.buildMessage("client::register", {
- ip: "localhost",
+ name: "桃夭Web",
clientId: self.taoyao.clientId,
+ clientType: "WEB",
signal: 100,
battery: battery.level * 100,
charging: battery.charging,
@@ -104,21 +109,21 @@ const signalChannel = {
// 开始心跳
self.heartbeat();
// 成功回调
- resolve(e);
+ resolve(self.channel);
};
- self.channel.onclose = function (e) {
- console.error("信令通道关闭", self.channel, e);
+ self.channel.onclose = async function () {
+ console.warn("信令通道关闭:", self.channel);
if (self.reconnection) {
self.reconnect();
}
- reject(e);
+ // 不要失败回调
};
- self.channel.onerror = function (e) {
- console.error("信令通道异常", self.channel, e);
+ self.channel.onerror = async function (e) {
+ console.error("信令通道异常:", self.channel, e);
if (self.reconnection) {
self.reconnect();
}
- reject(e);
+ // 不要失败回调
};
/**
* 回调策略:
@@ -127,7 +132,7 @@ const signalChannel = {
* 3. 如果前面所有回调没有返回true执行默认回调。
*/
self.channel.onmessage = function (e) {
- console.debug("信令通道消息", e.data);
+ console.debug("信令通道消息:", e.data);
let done = false;
const message = JSON.parse(e.data);
// 请求回调
@@ -154,22 +159,20 @@ const signalChannel = {
*/
reconnect() {
const self = this;
- if (self.lockReconnect) {
+ if (
+ self.lockReconnect ||
+ (self.channel && self.channel.readyState === WebSocket.OPEN)
+ ) {
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);
+ console.info("信令通道重连:", self.address);
+ self.connect(self.address, self.callback, self.reconnection);
self.lockReconnect = false;
}, self.connectionTimeout);
self.connectionTimeout = Math.min(
@@ -190,7 +193,11 @@ const signalChannel = {
self.callbackMapping.set(message.header.id, callback);
}
// 发送消息
- self.channel.send(JSON.stringify(message));
+ try {
+ self.channel.send(JSON.stringify(message));
+ } catch (error) {
+ console.error("推送消息异常:", message, error);
+ }
},
/**
* 同步请求
@@ -203,17 +210,23 @@ const signalChannel = {
const self = this;
return new Promise((resolve, reject) => {
let done = false;
- // 设置回调
+ // 注册回调
self.callbackMapping.set(message.header.id, (response) => {
resolve(response);
done = true;
+ // 返回true不要继续执行回调
return true;
});
- // 发送请求
- self.channel.send(JSON.stringify(message));
+ // 发送消息
+ try {
+ self.channel.send(JSON.stringify(message));
+ } catch (error) {
+ console.error("请求消息异常:", message, error);
+ }
// 设置超时
setTimeout(() => {
if (!done) {
+ self.callbackMapping.delete(message.header.id);
reject("请求超时", message);
}
}, 5000);
@@ -223,7 +236,7 @@ const signalChannel = {
* 关闭通道
*/
close() {
- let self = this;
+ const self = this;
self.reconnection = false;
self.channel.close();
clearTimeout(self.heartbeatTimer);
@@ -244,11 +257,14 @@ const signalChannel = {
case "client::reboot":
self.defaultClientReboot(message);
break;
+ case "client::shutdown":
+ self.defaultClientShutdown(message);
+ break;
case "client::register":
console.info("终端注册成功");
break;
case "platform::error":
- console.error("信令异常", message);
+ self.callbackError(message);
break;
}
},
@@ -262,13 +278,11 @@ const signalChannel = {
self.taoyao.audio = { ...defaultAudioConfig, ...message.body.media.audio };
self.taoyao.video = { ...defaultVideoConfig, ...message.body.media.video };
self.taoyao.webrtc = message.body.webrtc;
- self.taoyao.mediaServerList = message.body.media.mediaServerList;
console.debug(
"终端配置",
self.taoyao.audio,
self.taoyao.video,
- self.taoyao.webrtc,
- self.taoyao.mediaServerList
+ self.taoyao.webrtc
);
},
/**
@@ -280,6 +294,15 @@ const signalChannel = {
console.info("重启终端");
location.reload();
},
+ /**
+ * 终端重启默认回调
+ *
+ * @param {*} message 消息
+ */
+ defaultClientShutdown(message) {
+ console.info("关闭终端");
+ window.close();
+ },
};
/**
@@ -298,14 +321,14 @@ class Taoyao {
username;
// 信令密码
password;
+ // 回调事件
+ callback;
// 音频媒体配置
audio;
// 视频媒体配置
video;
// WebRTC配置
webrtc;
- // 媒体服务配置
- mediaServerList;
// 发送信令
push;
// 请求信令
@@ -382,8 +405,9 @@ class Taoyao {
*/
async buildSignal(callback) {
const self = this;
- signalChannel.taoyao = self;
+ self.callback = callback;
self.signalChannel = signalChannel;
+ signalChannel.taoyao = self;
// 不能直接this.push = this.signalChannel.push这样导致this对象错误
self.push = function (data, pushCallback) {
self.signalChannel.push(data, pushCallback);
@@ -397,27 +421,59 @@ class Taoyao {
);
}
/**
- * 打开媒体通道
- * TODO:共享 navigator.mediaDevices.getDisplayMedia();
+ * 错误回调
*/
- async buildMedia(roomId) {
- let self = this;
- // 释放资源
- self.closeMedia();
- if (roomId) {
- self.roomId = roomId;
+ callbackError(message, error) {
+ const self = this;
+ if (!self.callback) {
+ console.warn("没有注册回调:", message, error);
}
+ // 错误回调
+ self.callback(
+ protocol.buildMessage("platform::error", { message }, -9999),
+ error
+ );
+ }
+ /**
+ * 创建房间
+ */
+ async create(room) {
+ const self = this;
+ const response = await self.request(
+ protocol.buildMessage("room::create", room)
+ );
+ return response.body;
+ }
+ async enter(roomId) {
+ const self = this;
+ if (!roomId) {
+ this.callbackError("无效房间");
+ return;
+ }
+ self.roomId = roomId;
self.mediasoupDevice = new mediasoupClient.Device();
const response = await self.request(
protocol.buildMessage("media::router::rtp::capabilities", {
- roomId: roomId,
+ roomId: self.roomId,
+ })
+ );
+ const routerRtpCapabilities = response.body.rtpCapabilities;
+ await self.mediasoupDevice.load({ routerRtpCapabilities });
+ await self.request(
+ protocol.buildMessage("room::enter", {
+ roomId: roomId,
+ rtpCapabilities: self.consume
+ ? self.mediasoupDevice.rtpCapabilities
+ : undefined,
+ sctpCapabilities:
+ self.consume && self.useDataChannel
+ ? self.mediasoupDevice.sctpCapabilities
+ : undefined,
})
);
- const routerRtpCapabilities = response.body;
- self.mediasoupDevice.load({ routerRtpCapabilities });
- self.produceMedia();
}
/**
+ * TODO:共享 navigator.mediaDevices.getDisplayMedia();
* 生产媒体
* TODO:验证API试试修改媒体
* audioTrack.getSettings
@@ -426,7 +482,14 @@ class Taoyao {
*/
async produceMedia() {
const self = this;
+ if (!self.roomId) {
+ this.callbackError("无效房间");
+ return;
+ }
+ // 检查设备
self.checkDevice();
+ // 释放资源
+ self.closeMedia();
// TODO:暂时不知道为什么?
{
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
@@ -491,15 +554,16 @@ class Taoyao {
"produce",
async ({ kind, appData, rtpParameters }, callback, errback) => {
try {
- const { id } = await self.request(
+ const { producerId } = await self.request(
protocol.buildMessage("media::produce", {
kind,
+ roomId: self.roomId,
appData,
transportId: self.sendTransport.id,
rtpParameters,
})
);
- callback({ id });
+ callback({ id: producerId });
} catch (error) {
errback(error);
}
@@ -591,10 +655,11 @@ class Taoyao {
}
/**
* 生产音频
+ * TODO:重复点击
*/
async produceAudio() {
const self = this;
- if (this.produceAudio && this.mediasoupDevice.canProduce("audio")) {
+ if (self.audioProduce && self.mediasoupDevice.canProduce("audio")) {
if (this.audioProducer) {
return;
}
@@ -631,13 +696,13 @@ class Taoyao {
this.closeAudioProducer().catch(() => {});
});
} catch (error) {
- console.error("打开麦克风异常", error);
+ self.callbackError("麦克风打开异常", error);
if (track) {
track.stop();
}
}
} else {
- console.warn("音频打开失败");
+ self.callbackError("麦克风打开失败");
}
}
async closeAudioProducer() {
@@ -689,6 +754,7 @@ class Taoyao {
/**
* 生产视频
+ * TODO:重复点击
*/
async produceVideo() {}
/**
@@ -719,15 +785,40 @@ class Taoyao {
}
});
if (!audioEnabled && self.audioProduce) {
- throw new Error("没有音频媒体设备");
+ self.callbackError("没有音频媒体设备");
}
if (!videoEnabled && self.videoProduce) {
- throw new Error("没有视频媒体设备");
+ self.callbackError("没有视频媒体设备");
}
} else {
- throw new Error("没有媒体设备");
+ self.callbackError("没有媒体设备");
}
}
+
+ async restartIce() {
+ const self = this;
+ try {
+ if (self.sendTransport) {
+ const response = await self.request("media::ice::restart", {
+ roomId: self.roomId,
+ transportId: self.sendTransport.id,
+ });
+ const iceParameters = response.data.iceParameters;
+ await self.sendTransport.restartIce({ iceParameters });
+ }
+ if (self.recvTransport) {
+ const response = await self.request("media::ice::restart", {
+ roomId: self.roomId,
+ transportId: self.recvTransport.id,
+ });
+ const iceParameters = response.data.iceParameters;
+ await self.recvTransport.restartIce({ iceParameters });
+ }
+ } catch (error) {
+ self.callbackError("重启ICE失败", error);
+ }
+ }
+
/**
* 关闭媒体
*/
diff --git a/taoyao-signal-server/pom.xml b/taoyao-signal-server/pom.xml
index 6d51827..30fe99b 100644
--- a/taoyao-signal-server/pom.xml
+++ b/taoyao-signal-server/pom.xml
@@ -72,11 +72,6 @@
org.springframework.boot
spring-boot-starter-aop
-
-
- org.springframework.boot
- spring-boot-autoconfigure
-
org.springframework.boot
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/annotation/Listener.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/annotation/Listener.java
new file mode 100644
index 0000000..c5cac65
--- /dev/null
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/annotation/Listener.java
@@ -0,0 +1,22 @@
+package com.acgist.taoyao.boot.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * 监听器
+ *
+ * @author acgist
+ */
+@Target(ElementType.TYPE)
+@Component
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Listener {
+
+}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/Constant.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/Constant.java
index 3ebf288..a65d158 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/Constant.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/Constant.java
@@ -20,9 +20,29 @@ public interface Constant {
*/
String TO = "to";
/**
- * 发送方
+ * 状态
*/
- String FROM = "from";
+ String STATUS = "status";
+ /**
+ * 配置
+ */
+ String CONFIG = "config";
+ /**
+ * 纬度
+ */
+ String LATITUDE = "latitude";
+ /**
+ * 经度
+ */
+ String LONGITUDE = "longitude";
+ /**
+ * 湿度
+ */
+ String HUMIDITY = "humidity";
+ /**
+ * 温度
+ */
+ String TEMPERATURE = "temperature";
/**
* 信号强度(0~100)
*/
@@ -31,10 +51,18 @@ public interface Constant {
* 电池电量(0~100)
*/
String BATTERY = "battery";
+ /**
+ * 是否正在运行
+ */
+ String RUNNING = "running";
/**
* 是否正在充电
*/
String CHARGING = "charging";
+ /**
+ * 是否正在录像
+ */
+ String RECORDING = "recording";
/**
* 地址
*/
@@ -75,6 +103,10 @@ public interface Constant {
* 名称
*/
String NAME = "name";
+ /**
+ * 类型
+ */
+ String CLIENT_TYPE = "clientType";
/**
* 脚本
*/
@@ -83,6 +115,10 @@ public interface Constant {
* 结果
*/
String RESULT = "result";
+ /**
+ * 消息
+ */
+ String MESSAGE = "message";
/**
* 请求
*/
@@ -95,6 +131,10 @@ public interface Constant {
* 密码
*/
String PASSWORD = "password";
+ /**
+ * 媒体类型
+ */
+ String KIND = "kind";
/**
* 媒体
*/
@@ -107,12 +147,6 @@ public interface Constant {
* 日期时间
*/
String DATETIME = "datetime";
- /**
- * PeerId
- *
- * @see #CLIENT_ID
- */
- String PEER_ID = "peerId";
/**
* 房间ID
*/
@@ -121,10 +155,12 @@ public interface Constant {
* 媒体服务ID
*/
String MEDIA_ID = "mediaId";
+ /**
+ * 媒体流ID
+ */
+ String STREAM_ID = "streamId";
/**
* 终端ID
- *
- * @see #PEER_ID
*/
String CLIENT_ID = "clientId";
/**
@@ -171,5 +207,21 @@ public interface Constant {
* SCTP参数
*/
String SCTP_PARAMETERS = "sctpParameters";
+ /**
+ * RTP能力
+ */
+ String RTP_CAPABILITIES = "rtpCapabilities";
+ /**
+ * SCTP能力
+ */
+ String SCTP_CAPABILITIES = "sctpCapabilities";
+ /**
+ * 生产者
+ */
+ String CONSUMING = "consuming";
+ /**
+ * 消费者
+ */
+ String PRODUCING = "producing";
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteProperties.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteProperties.java
new file mode 100644
index 0000000..99b1262
--- /dev/null
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteProperties.java
@@ -0,0 +1,30 @@
+package com.acgist.taoyao.boot.config;
+
+import java.util.List;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 地址重写
+ * 内外网多网卡环境重写网络号保留主机号,通过重新地址实现多网互通。
+ *
+ * @author acgist
+ */
+@Getter
+@Setter
+@Schema(title = "地址重写", description = "地址重写")
+@ConfigurationProperties(prefix = "taoyao.ip-rewrite")
+public class IpRewriteProperties {
+
+ @Schema(title = "是否启用", description = "是否启用")
+ private Boolean enabled;
+ @Schema(title = "子网掩码", description = "子网掩码:主机号的长度")
+ private Integer prefix;
+ @Schema(title = "重写规则", description = "重写规则")
+ private List rule;
+
+}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteRuleProperties.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteRuleProperties.java
new file mode 100644
index 0000000..8df45f4
--- /dev/null
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/IpRewriteRuleProperties.java
@@ -0,0 +1,27 @@
+package com.acgist.taoyao.boot.config;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 重写规则
+ *
+ * @author acgist
+ */
+/**
+ * WebRTC STUN配置
+ *
+ * @author acgist
+ */
+@Getter
+@Setter
+@Schema(title = "重写规则", description = "重写规则")
+public class IpRewriteRuleProperties {
+
+ @Schema(title = "网络号", description = "网络号:匹配终端IP")
+ private String network;
+ @Schema(title = "目标地址", description = "目标地址:没有配置等于网络号加上原始主机号")
+ private String targetHost;
+
+}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaProperties.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaProperties.java
index f4ea7d3..3690878 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaProperties.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaProperties.java
@@ -1,7 +1,5 @@
package com.acgist.taoyao.boot.config;
-import java.util.List;
-
import org.springframework.boot.context.properties.ConfigurationProperties;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -33,7 +31,5 @@ public class MediaProperties {
private MediaVideoProperties hdVideo;
@Schema(title = "标清视频配置", description = "标清视频配置")
private MediaVideoProperties sdVideo;
- @Schema(title = "媒体服务配置", description = "媒体服务配置")
- private List mediaServerList;
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaServerProperties.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaServerProperties.java
deleted file mode 100644
index 0d79543..0000000
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaServerProperties.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.acgist.taoyao.boot.config;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * 媒体服务配置
- *
- * @author acgist
- */
-@Getter
-@Setter
-@Schema(title = "媒体服务配置", description = "媒体服务配置")
-public class MediaServerProperties {
-
- @Schema(title = "媒体服务标识", description = "媒体服务标识")
- private String mediaId;
- @Schema(title = "是否启用", description = "是否启用")
- private Boolean enabled;
- @Schema(title = "是否启用重写本地IP", description = "内外网多网卡环境重写IP地址")
- private Boolean rewriteIp;
- @Schema(title = "主机", description = "主机")
- private String host;
- @Schema(title = "端口", description = "端口")
- private Integer port;
- @Schema(title = "协议", description = "协议")
- private String schema;
- @Schema(title = "用户", description = "用户")
- private String username;
- @Schema(title = "密码", description = "密码")
- private String password;
-
- @Schema(title = "完整地址", description = "完整地址")
- public String getAddress() {
- return this.schema + "://" + this.host + ":" + this.port;
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/ScriptProperties.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/ScriptProperties.java
index 1137e16..bd4b8a3 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/ScriptProperties.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/ScriptProperties.java
@@ -19,10 +19,6 @@ public class ScriptProperties {
@Schema(title = "是否启用", description = "是否启用")
private Boolean enabled;
- @Schema(title = "重启媒体服务", description = "重启媒体服务")
- private String mediaReboot;
- @Schema(title = "关闭媒体服务", description = "关闭媒体服务")
- private String mediaShutdown;
@Schema(title = "重启系统", description = "重启系统")
private String systemReboot;
@Schema(title = "关闭系统", description = "关闭系统")
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/TaoyaoProperties.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/TaoyaoProperties.java
index 13642d6..5e52fae 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/TaoyaoProperties.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/TaoyaoProperties.java
@@ -25,8 +25,6 @@ public class TaoyaoProperties {
private String version;
@Schema(title = "项目描述", description = "项目描述")
private String description;
- @Schema(title = "子网掩码", description = "子网掩码")
- private Integer ipMask;
@Schema(title = "超时时间", description = "超时时间")
private Long timeout;
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/BootAutoConfiguration.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/BootAutoConfiguration.java
index c24e1ff..d0c005b 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/BootAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/BootAutoConfiguration.java
@@ -10,9 +10,8 @@ import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration;
@@ -21,7 +20,6 @@ import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.boot.task.TaskSchedulerBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
@@ -46,6 +44,7 @@ import org.springframework.web.multipart.support.MissingServletRequestPartExcept
import org.springframework.web.servlet.NoHandlerFoundException;
import com.acgist.taoyao.boot.config.IdProperties;
+import com.acgist.taoyao.boot.config.IpRewriteProperties;
import com.acgist.taoyao.boot.config.MediaProperties;
import com.acgist.taoyao.boot.config.ScriptProperties;
import com.acgist.taoyao.boot.config.SecurityProperties;
@@ -57,18 +56,19 @@ import com.acgist.taoyao.boot.controller.TaoyaoErrorController;
import com.acgist.taoyao.boot.model.MessageCode;
import com.acgist.taoyao.boot.runner.OrderedCommandLineRunner;
import com.acgist.taoyao.boot.service.IdService;
-import com.acgist.taoyao.boot.service.IpService;
import com.acgist.taoyao.boot.service.impl.IdServiceImpl;
-import com.acgist.taoyao.boot.service.impl.IpServiceImpl;
import com.acgist.taoyao.boot.utils.ErrorUtils;
import com.acgist.taoyao.boot.utils.FileUtils;
import com.acgist.taoyao.boot.utils.HTTPUtils;
import com.acgist.taoyao.boot.utils.JSONUtils;
+import com.acgist.taoyao.boot.utils.NetUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import ch.qos.logback.classic.LoggerContext;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
+import jakarta.validation.ConstraintViolationException;
+import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
/**
@@ -80,8 +80,8 @@ import lombok.extern.slf4j.Slf4j;
@Order(Ordered.HIGHEST_PRECEDENCE)
@Import({ TaskExecutionAutoConfiguration.class, TaskSchedulingAutoConfiguration.class })
@EnableAsync
-@Configuration
@EnableScheduling
+@AutoConfiguration
@EnableAspectJAutoProxy(exposeProxy = false)
@EnableConfigurationProperties({
IdProperties.class,
@@ -90,26 +90,23 @@ import lombok.extern.slf4j.Slf4j;
SocketProperties.class,
TaoyaoProperties.class,
WebrtcProperties.class,
- SecurityProperties.class
+ SecurityProperties.class,
+ IpRewriteProperties.class
})
public class BootAutoConfiguration {
-
- @Value("${spring.application.name:taoyao}")
- private String name;
- @Autowired
- private ApplicationContext applicationContext;
+ private final TaoyaoProperties taoyaoProperties;
+ private final ApplicationContext applicationContext;
- @Bean
- @ConditionalOnMissingBean
- public IdService idService() {
- return new IdServiceImpl();
+ public BootAutoConfiguration(TaoyaoProperties taoyaoProperties, ApplicationContext applicationContext) {
+ this.taoyaoProperties = taoyaoProperties;
+ this.applicationContext = applicationContext;
}
@Bean
@ConditionalOnMissingBean
- public IpService ipService() {
- return new IpServiceImpl();
+ public IdService idService(IdProperties idProperties) {
+ return new IdServiceImpl(idProperties);
}
@Bean
@@ -146,21 +143,17 @@ public class BootAutoConfiguration {
}
@Bean
- @Autowired
public CommandLineRunner successCommandLineRunner(
TaskExecutor taskExecutor,
- TaoyaoProperties taoyaoProperties
+ TaoyaoProperties taoyaoProperties,
+ IpRewriteProperties ipRewriteProperties
) {
return new OrderedCommandLineRunner() {
- @Override
- public int getOrder() {
- return Integer.MAX_VALUE;
- }
@Override
public void run(String ... args) throws Exception {
+ NetUtils.init(ipRewriteProperties);
HTTPUtils.init(taoyaoProperties.getTimeout(), taskExecutor);
BootAutoConfiguration.this.registerException();
- log.info("项目启动成功:{}", BootAutoConfiguration.this.name);
}
};
}
@@ -190,10 +183,10 @@ public class BootAutoConfiguration {
log.info("临时目录:{}", System.getProperty("java.io.tmpdir"));
log.info("文件编码:{}", System.getProperty("file.encoding"));
this.applicationContext.getBeansOfType(TaskExecutor.class).forEach((k, v) -> {
- log.info("系统任务线程池:{}-{}", k, v);
+ log.info("系统任务线程池:{} - {}", k, v);
});
this.applicationContext.getBeansOfType(TaskScheduler.class).forEach((k, v) -> {
- log.info("系统定时任务线程池:{}-{}", k, v);
+ log.info("系统定时任务线程池:{} - {}", k, v);
});
}
@@ -202,9 +195,11 @@ public class BootAutoConfiguration {
*/
public void registerException() {
ErrorUtils.register(MessageCode.CODE_3400, BindException.class);
+ ErrorUtils.register(MessageCode.CODE_3400, ValidationException.class);
ErrorUtils.register(MessageCode.CODE_3400, TypeMismatchException.class);
ErrorUtils.register(MessageCode.CODE_3404, NoHandlerFoundException.class);
ErrorUtils.register(MessageCode.CODE_3503, AsyncRequestTimeoutException.class);
+ ErrorUtils.register(MessageCode.CODE_3400, ConstraintViolationException.class);
ErrorUtils.register(MessageCode.CODE_3500, MissingPathVariableException.class);
ErrorUtils.register(MessageCode.CODE_3400, ServletRequestBindingException.class);
ErrorUtils.register(MessageCode.CODE_3400, HttpMessageNotReadableException.class);
@@ -220,7 +215,7 @@ public class BootAutoConfiguration {
@PreDestroy
public void destroy() {
- log.info("系统关闭:{}", this.name);
+ log.info("系统关闭:{}", this.taoyaoProperties.getName());
// 刷出日志缓存
final ILoggerFactory factory = LoggerFactory.getILoggerFactory();
if (factory instanceof LoggerContext context) {
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/SpringDocAutoConfiguration.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/SpringDocAutoConfiguration.java
index 9ad2632..f025526 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/SpringDocAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/SpringDocAutoConfiguration.java
@@ -3,12 +3,11 @@ package com.acgist.taoyao.boot.configuration;
import java.util.List;
import org.springdoc.core.models.GroupedOpenApi;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import com.acgist.taoyao.boot.config.TaoyaoProperties;
@@ -27,20 +26,20 @@ import io.swagger.v3.oas.models.security.SecurityScheme;
* @author acgist
*/
@Profile({ "dev", "sit" })
-@Configuration
+@AutoConfiguration
@ConditionalOnClass(OpenAPI.class)
public class SpringDocAutoConfiguration {
-
- /**
- * Basic认证
- */
- private static final String BASIC = "Basic";
-
- @Value("${server.port:8888}")
- private Integer port;
-
- @Autowired
- private TaoyaoProperties taoyaoProperties;
+
+ @Value("${server.port:8888}")
+ private Integer port;
+ @Value("${taoyao.security.scheme:Basic}")
+ private String scheme;
+
+ private final TaoyaoProperties taoyaoProperties;
+
+ public SpringDocAutoConfiguration(TaoyaoProperties taoyaoProperties) {
+ this.taoyaoProperties = taoyaoProperties;
+ }
@Bean
public GroupedOpenApi signalApi() {
@@ -106,7 +105,7 @@ public class SpringDocAutoConfiguration {
private List buildSecurity() {
return List.of(
new SecurityRequirement()
- .addList(BASIC)
+ .addList(this.scheme)
);
}
@@ -115,7 +114,7 @@ public class SpringDocAutoConfiguration {
*/
private Components buildComponents() {
return new Components()
- .addSecuritySchemes(BASIC, this.buildSecurityScheme());
+ .addSecuritySchemes(this.scheme, this.buildSecurityScheme());
}
/**
@@ -123,8 +122,8 @@ public class SpringDocAutoConfiguration {
*/
private SecurityScheme buildSecurityScheme() {
return new SecurityScheme()
- .name(BASIC)
- .scheme(BASIC)
+ .name(this.scheme)
+ .scheme(this.scheme)
.in(SecurityScheme.In.HEADER)
.type(SecurityScheme.Type.HTTP);
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/WebMvcConfigurerAutoConfiguration.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/WebMvcConfigurerAutoConfiguration.java
index bf646fd..f1d9f4f 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/WebMvcConfigurerAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/configuration/WebMvcConfigurerAutoConfiguration.java
@@ -1,8 +1,7 @@
package com.acgist.taoyao.boot.configuration;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -13,16 +12,21 @@ import lombok.extern.slf4j.Slf4j;
/**
* MVC自动配置
*
+ * TODO:ErrorPageRegistrar拦截400错误https://localhost:8888/?\%3E%3C
+ *
* @author acgist
*/
@Slf4j
-@Configuration
+@AutoConfiguration
public class WebMvcConfigurerAutoConfiguration implements WebMvcConfigurer {
- @Autowired
- private ApplicationContext applicationContext;
+ private final ApplicationContext applicationContext;
- @Override
+ public WebMvcConfigurerAutoConfiguration(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
public void addInterceptors(InterceptorRegistry registry) {
this.applicationContext.getBeansOfType(InterceptorAdapter.class).entrySet().stream()
.sorted((a, z) -> a.getValue().compareTo(z.getValue()))
@@ -32,5 +36,10 @@ public class WebMvcConfigurerAutoConfiguration implements WebMvcConfigurer {
registry.addInterceptor(value).addPathPatterns(value.pathPattern());
});
}
-
+
+// @Override
+// public void addCorsMappings(CorsRegistry registry) {
+// WebMvcConfigurer.super.addCorsMappings(registry);
+// }
+
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Header.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Header.java
index d025dee..463d92d 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Header.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Header.java
@@ -33,7 +33,7 @@ public class Header implements Serializable {
* 消息标识
*/
@Schema(title = "消息标识", description = "消息标识")
- private String id;
+ private Long id;
/**
* 信令标识
*/
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java
index 714a287..14909bf 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java
@@ -1,7 +1,7 @@
package com.acgist.taoyao.boot.model;
import java.io.Serializable;
-import java.util.HashMap;
+import java.util.Map;
import org.apache.commons.lang3.StringUtils;
@@ -170,26 +170,27 @@ public class Message implements Cloneable, Serializable {
return new Message(this.code, this.message, this.header.clone(), this.body);
}
- /**
- * 克隆消息设置Map消息主体
- *
- * @return 克隆消息
- */
- public Message CloneWithMapBody() {
- final Message message = this.clone();
- message.setBody(new HashMap<>());
- return message;
- }
-
/**
* 克隆消息排除消息主体
*
* @return 克隆消息
*/
public Message cloneWithoutBody() {
- final Message message = this.clone();
- message.setBody(null);
- return message;
+ return new Message(this.code, this.message, this.header.clone(), null);
+ }
+
+ /**
+ * @return Map消息主体
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public Map mapBody() {
+ if(this.body instanceof Map map) {
+ return map;
+ } else if(this.body == null) {
+ return Map.of();
+ } else {
+ throw MessageCodeException.of("信令主体类型错误:" + this.body);
+ }
}
@Override
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java
index 7b7a63e..3840739 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java
@@ -2,23 +2,24 @@ package com.acgist.taoyao.boot.service;
/**
* ID生成器
+ * 只能用于生成消息标识
*
* @author acgist
*/
public interface IdService {
/**
- * 生成十九位的ID:YYMMddHHmmss(12) + index(1) + xxxxxx(6)
+ * 生成十六位的ID:ddHHmmss(8) + index(2) + xxxxxx(6)
*
* @return ID
*/
long buildId();
/**
- * @see #buildId()
+ * 生成唯一ID
*
* @return ID
*/
- String buildIdToString();
-
+ String buildUuid();
+
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IpService.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IpService.java
deleted file mode 100644
index dd21d19..0000000
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IpService.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.acgist.taoyao.boot.service;
-
-/**
- * 内外网多网卡环境IP服务
- * 支持配置域名
- *
- * @author acgist
- */
-public interface IpService {
-
- /**
- * @see #subnetIp(String, String, String)
- */
- default boolean subnetIp(String sourceIp, String clientIp) {
- return this.subnetIp(sourceIp, clientIp, null);
- }
-
- /**
- * 内网是否相同网段
- *
- * @param sourceIp 原始IP
- * @param clientIp 终端IP
- * @param defaultSourceIp 默认原始IP
- *
- * @return 是否匹配
- */
- boolean subnetIp(String sourceIp, String clientIp, String defaultSourceIp);
-
- /**
- * @see #rewriteIp(String, String, String)
- */
- default String rewriteIp(String sourceIp, String clientIp) {
- return this.rewriteIp(sourceIp, clientIp, null);
- }
-
- /**
- * 重写IP地址
- * 内网环境重写IP地址:修改网络号保留主机号
- *
- * @param sourceIp 原始IP
- * @param clientIp 终端IP
- * @param defaultSourceIp 默认原始IP
- *
- * @return 替换IP
- */
- String rewriteIp(String sourceIp, String clientIp, String defaultSourceIp);
-
-}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java
index 77dd103..81114d0 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java
@@ -1,23 +1,25 @@
package com.acgist.taoyao.boot.service.impl;
import java.time.LocalDateTime;
-
-import org.springframework.beans.factory.annotation.Autowired;
+import java.util.UUID;
import com.acgist.taoyao.boot.config.IdProperties;
import com.acgist.taoyao.boot.service.IdService;
public class IdServiceImpl implements IdService {
+ private final IdProperties idProperties;
+
+ public IdServiceImpl(IdProperties idProperties) {
+ this.idProperties = idProperties;
+ }
+
/**
* 当前索引
*/
private int index;
-
- @Autowired
- private IdProperties idProperties;
- @Override
+ @Override
public long buildId() {
synchronized (this) {
if (++this.index > this.idProperties.getMaxIndex()) {
@@ -26,21 +28,18 @@ public class IdServiceImpl implements IdService {
}
final LocalDateTime time = LocalDateTime.now();
return
- 100000000000000000L * (time.getYear() % 100) +
- 1000000000000000L * time.getMonthValue() +
- 10000000000000L * time.getDayOfMonth() +
- 100000000000L * time.getHour() +
- 1000000000L * time.getMinute() +
- 10000000L * time.getSecond() +
- // 机器序号一位
+ 100000000000000L * time.getDayOfMonth() +
+ 1000000000000L * time.getHour() +
+ 10000000000L * time.getMinute() +
+ 100000000L * time.getSecond() +
1000000L * this.idProperties.getIndex() +
// 每秒并发数量
this.index;
}
-
- @Override
- public String buildIdToString() {
- return String.valueOf(this.buildId());
- }
+
+ @Override
+ public String buildUuid() {
+ return UUID.randomUUID().toString();
+ }
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IpServiceImpl.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IpServiceImpl.java
deleted file mode 100644
index d3ae136..0000000
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IpServiceImpl.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package com.acgist.taoyao.boot.service.impl;
-
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.BitSet;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.acgist.taoyao.boot.config.TaoyaoProperties;
-import com.acgist.taoyao.boot.service.IpService;
-
-import jakarta.annotation.PostConstruct;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-public class IpServiceImpl implements IpService {
-
- @Autowired
- private TaoyaoProperties taoyaoProperties;
-
- /**
- * 本机IP
- */
- private String localIp;
- /**
- * 本机环回IP
- */
- private String loopbackIp;
-
- @PostConstruct
- public void init() {
- try {
- final InetAddress localHost = InetAddress.getLocalHost();
- final InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
- this.localIp = localHost.getHostAddress();
- this.loopbackIp = loopbackAddress.getHostAddress();
- log.info("本机IP:{}", this.localIp);
- log.info("环回IP:{}", this.loopbackIp);
- } catch (UnknownHostException e) {
- log.error("获取本机IP地址异常", e);
- }
- }
-
- @Override
- public boolean subnetIp(final String sourceIp, final String clientIp, final String defaultSourceIp) {
- try {
- InetAddress clientAddress = InetAddress.getByName(clientIp);
- // 通配地址或者本地地址替换本机IP
- if(this.localAnyOrLoopAddress(clientAddress)) {
- clientAddress = InetAddress.getByName(this.localIp);
- }
- InetAddress sourceAddress = InetAddress.getByName(sourceIp);
- // 通配地址或者本地换回地址替换默认地址
- if(StringUtils.isNotEmpty(defaultSourceIp) && this.localAnyOrLoopAddress(sourceAddress)) {
- sourceAddress = InetAddress.getByName(defaultSourceIp);
- }
- if(this.localAddress(sourceAddress) && this.localAddress(clientAddress)) {
- final byte[] sourceBytes = sourceAddress.getAddress();
- final byte[] clientBytes = clientAddress.getAddress();
- final int length = (sourceBytes.length & clientBytes.length) * Byte.SIZE;
- final BitSet sourceBit = BitSet.valueOf(sourceBytes);
- final BitSet clientBit = BitSet.valueOf(clientBytes);
- sourceBit.set(this.taoyaoProperties.getIpMask(), length, true);
- clientBit.set(this.taoyaoProperties.getIpMask(), length, true);
- final BigInteger source = new BigInteger(sourceBit.toByteArray());
- final BigInteger client = new BigInteger(clientBit.toByteArray());
- return source.equals(client);
- }
- } catch (Exception e) {
- log.error("IP地址转换异常:{}-{}", sourceIp, clientIp, e);
- }
- return true;
- }
-
- @Override
- public String rewriteIp(final String sourceIp, final String clientIp, final String defaultSourceIp) {
- try {
- InetAddress clientAddress = InetAddress.getByName(clientIp);
- // 通配地址或者本地地址替换本机IP
- if(this.localAnyOrLoopAddress(clientAddress)) {
- clientAddress = InetAddress.getByName(this.localIp);
- }
- InetAddress sourceAddress = InetAddress.getByName(sourceIp);
- // 通配地址或者本地换回地址替换默认地址
- if(this.localAnyOrLoopAddress(sourceAddress) && StringUtils.isNotEmpty(defaultSourceIp)) {
- sourceAddress = InetAddress.getByName(defaultSourceIp);
- }
- if(this.localAddress(sourceAddress) && this.localAddress(clientAddress)) {
- final byte[] sourceBytes = sourceAddress.getAddress();
- final byte[] clientBytes = clientAddress.getAddress();
- final int length = (sourceBytes.length & clientBytes.length) * Byte.SIZE;
- final BitSet sourceBit = BitSet.valueOf(sourceBytes);
- final BitSet clientBit = BitSet.valueOf(clientBytes);
- // 替换网络号保留主机号
- for (int index = 0; index < this.taoyaoProperties.getIpMask(); index++) {
- sourceBit.set(index, clientBit.get(index));
- }
- final byte[] bytes = sourceBit.toByteArray();
- if(bytes.length < length) {
- final byte[] fillBytes = new byte[length / Byte.SIZE];
- System.arraycopy(bytes, 0, fillBytes, 0, bytes.length);
- return InetAddress.getByAddress(fillBytes).getHostAddress();
- } else {
- return InetAddress.getByAddress(bytes).getHostAddress();
- }
- }
- } catch (Exception e) {
- log.error("IP地址转换异常:{}-{}", sourceIp, clientIp, e);
- }
- return sourceIp;
- }
-
- /**
- * @param inetAddress IP地址
- *
- * @return 通配或者换回地址
- */
- private boolean localAnyOrLoopAddress(InetAddress inetAddress) {
- return
- inetAddress.isAnyLocalAddress() ||
- inetAddress.isLoopbackAddress();
- }
-
- /**
- * @param inetAddress IP地址
- *
- * @return 本地IP
- */
- private boolean localAddress(InetAddress inetAddress) {
- return
- // 通配地址:0.0.0.0
- inetAddress.isAnyLocalAddress() ||
- // 环回地址:127.0.0.1 | 0:0:0:0:0:0:0:1
- inetAddress.isLoopbackAddress() ||
- // 链接地址:虚拟网卡
- inetAddress.isLinkLocalAddress() ||
- // 组播地址
- inetAddress.isMulticastAddress() ||
- // 本地地址:A/B/C类本地地址
- inetAddress.isSiteLocalAddress();
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java
index 6c6ddab..a81297c 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java
@@ -1,7 +1,6 @@
package com.acgist.taoyao.boot.utils;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -9,7 +8,6 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
-import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
@@ -19,6 +17,8 @@ import com.acgist.taoyao.boot.model.MessageCodeException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
/**
@@ -65,7 +65,7 @@ public final class ErrorUtils {
* @param clazz 异常类型
*/
public static final void register(MessageCode code, Class> clazz) {
- log.info("注册状态编码异常映射:{}-{}", code, clazz);
+ log.debug("注册状态编码异常映射:{} - {}", code, clazz);
synchronized (CODE_MAPPING) {
CODE_MAPPING.put(clazz, code);
}
@@ -140,6 +140,11 @@ public final class ErrorUtils {
原始信息:{}
""", path, query, method, message, status, globalError);
}
+// final Map body = new HashMap<>();
+// body.put("url", path);
+// body.put("query", query);
+// body.put("method", method);
+// message.setBody(body);
return message;
}
@@ -204,24 +209,26 @@ public final class ErrorUtils {
* @return 异常信息
*/
public static final String message(MessageCode messageCode, Throwable throwable) {
- // 数据校验异常:ValidationException
- if(
- throwable instanceof BindException ||
- throwable instanceof MethodArgumentNotValidException
- ) {
- final BindException bindException = (BindException) throwable;
- final List allErrors = bindException.getAllErrors();
- return allErrors.stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(","));
+ if(throwable instanceof BindException bindException) {
+ return bindException.getAllErrors().stream()
+ .map(ObjectError::getDefaultMessage)
+ .collect(Collectors.joining(" && "));
+ }
+ if(throwable instanceof ConstraintViolationException violationException) {
+ return violationException.getConstraintViolations().stream()
+ .map(ConstraintViolation::getMessage)
+ .collect(Collectors.joining(" && "));
}
- // 为了系统安全建议不要直接返回
final String message = throwable.getMessage();
- if(StringUtils.isNotEmpty(message) && messageCode == MessageCode.CODE_9999) {
+ // 不是未知系统异常
+ if(StringUtils.isNotEmpty(message) && messageCode != MessageCode.CODE_9999) {
return message;
}
- // 匹配含有中文
+ // 包含中文直接返回:自定义的错误
if(StringUtils.isNotEmpty(message) && message.matches(".*[\\u4e00-\\u9fa5]+.*")) {
return message;
}
+ // 其他情况不能直接返回异常信息
return messageCode.getMessage();
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java
index 42c0127..e9a8b6e 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java
@@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
@@ -211,7 +210,8 @@ public final class JSONUtils {
*/
private static final Module buildCustomModule() {
final SimpleModule customModule = new SimpleModule("CustomModule");
- customModule.addSerializer(Long.class, ToStringSerializer.instance);
+ // 注意不能转换Long类型数据:请求数据类型变化
+// customModule.addSerializer(Long.class, ToStringSerializer.instance);
return customModule;
}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/MapUtils.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/MapUtils.java
new file mode 100644
index 0000000..6f3b953
--- /dev/null
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/MapUtils.java
@@ -0,0 +1,106 @@
+package com.acgist.taoyao.boot.utils;
+
+import java.util.Map;
+
+/**
+ * Map工具
+ *
+ * @author acgist
+ */
+public final class MapUtils {
+
+ private MapUtils() {
+ }
+
+ /**
+ * @param 参数泛型
+ *
+ * @param body 消息主体
+ * @param key 参数名称
+ *
+ * @return 参数值
+ */
+ @SuppressWarnings("unchecked")
+ public static final T get(Map, ?> body, String key) {
+ if(body == null) {
+ return null;
+ }
+ return (T) body.get(key);
+ }
+
+ /**
+ * @param 参数泛型
+ *
+ * @param body 消息主体
+ * @param key 参数名称
+ * @param defaultValue 参数默认值
+ *
+ * @return 参数值
+ */
+ @SuppressWarnings("unchecked")
+ public static final T get(Map, ?> body, String key, T defaultValue) {
+ if(body == null) {
+ return defaultValue;
+ }
+ final T t = (T) body.get(key);
+ return t == null ? defaultValue : t;
+ }
+
+ /**
+ * @param body 消息主体
+ * @param key 参数名称
+ *
+ * @return 参数值
+ */
+ public static final Long getLong(Map, ?> body, String key) {
+ if(body == null) {
+ return null;
+ }
+ final Object object = body.get(key);
+ if(object == null) {
+ return null;
+ } else if(object instanceof Long value) {
+ return value;
+ }
+ return Long.valueOf(object.toString());
+ }
+
+ /**
+ * @param body 消息主体
+ * @param key 参数名称
+ *
+ * @return 参数值
+ */
+ public static final Integer getInteger(Map, ?> body, String key) {
+ if(body == null) {
+ return null;
+ }
+ final Object object = body.get(key);
+ if(object == null) {
+ return null;
+ } else if(object instanceof Integer value) {
+ return value;
+ }
+ return Integer.valueOf(object.toString());
+ }
+
+ /**
+ * @param body 消息主体
+ * @param key 参数名称
+ *
+ * @return 参数值
+ */
+ public static final Boolean getBoolean(Map, ?> body, String key) {
+ if(body == null) {
+ return null;
+ }
+ final Object object = body.get(key);
+ if(object == null) {
+ return null;
+ } else if(object instanceof Boolean value) {
+ return value;
+ }
+ return Boolean.valueOf(object.toString());
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/NetUtils.java b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/NetUtils.java
new file mode 100644
index 0000000..c11e210
--- /dev/null
+++ b/taoyao-signal-server/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/NetUtils.java
@@ -0,0 +1,198 @@
+package com.acgist.taoyao.boot.utils;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.BitSet;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.acgist.taoyao.boot.config.IpRewriteProperties;
+import com.acgist.taoyao.boot.config.IpRewriteRuleProperties;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 网络工具
+ *
+ * @author acgist
+ */
+@Slf4j
+public final class NetUtils {
+
+ private NetUtils() {
+ }
+
+ /**
+ * 本机IP
+ */
+ private static String localIp;
+ /**
+ * 环回IP
+ */
+ private static String loopbackIp;
+ /**
+ * 地址重写
+ */
+ private static IpRewriteProperties ipRewriteProperties;
+
+ /**
+ * 加载配置
+ */
+ public static final void init(IpRewriteProperties ipRewriteProperties) {
+ try {
+ NetUtils.ipRewriteProperties = ipRewriteProperties;
+ final InetAddress localHost = InetAddress.getLocalHost();
+ final InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
+ NetUtils.localIp = localHost.getHostAddress();
+ NetUtils.loopbackIp = loopbackAddress.getHostAddress();
+ log.info("本机IP:{}", NetUtils.localIp);
+ log.info("环回IP:{}", NetUtils.loopbackIp);
+ } catch (UnknownHostException e) {
+ log.error("加载网络配置异常", e);
+ }
+ }
+
+ /**
+ * @return 本机IP
+ */
+ public static final String localIp() {
+ return NetUtils.localIp;
+ }
+
+ /**
+ * @return 环回IP
+ */
+ public static final String loopbackIp() {
+ return NetUtils.loopbackIp;
+ }
+
+ /**
+ * 内网是否相同网段
+ *
+ * @param sourceIp 原始IP
+ * @param clientIp 终端IP
+ *
+ * @return 是否匹配
+ */
+ public static final boolean subnetIp(final String sourceIp, final String clientIp) {
+ try {
+ final InetAddress sourceAddress = NetUtils.realAddress(sourceIp);
+ final InetAddress clientAddress = NetUtils.realAddress(clientIp);
+ if(NetUtils.localAddress(sourceAddress) && NetUtils.localAddress(clientAddress)) {
+ final byte[] sourceBytes = sourceAddress.getAddress();
+ final byte[] clientBytes = clientAddress.getAddress();
+ final int length = (sourceBytes.length & clientBytes.length) * Byte.SIZE;
+ final BitSet sourceBit = BitSet.valueOf(sourceBytes);
+ final BitSet clientBit = BitSet.valueOf(clientBytes);
+ sourceBit.set(NetUtils.ipRewriteProperties.getPrefix(), length, true);
+ clientBit.set(NetUtils.ipRewriteProperties.getPrefix(), length, true);
+ final BigInteger source = new BigInteger(sourceBit.toByteArray());
+ final BigInteger client = new BigInteger(clientBit.toByteArray());
+ return source.equals(client);
+ }
+ } catch (UnknownHostException e) {
+ log.error("IP地址转换异常:{}-{}", sourceIp, clientIp, e);
+ }
+ return true;
+ }
+
+ /**
+ * 重写地址
+ *
+ * @param sourceIp 原始IP
+ * @param clientIp 终端IP
+ *
+ * @return 替换IP
+ */
+ public static final String rewriteIp(final String sourceIp, final String clientIp) {
+ if(Boolean.FALSE.equals(NetUtils.ipRewriteProperties.getEnabled())) {
+ return sourceIp;
+ }
+ try {
+ final InetAddress sourceAddress = NetUtils.realAddress(sourceIp);
+ final InetAddress clientAddress = NetUtils.realAddress(clientIp);
+ if(NetUtils.localAddress(sourceAddress) && NetUtils.localAddress(clientAddress)) {
+ final IpRewriteRuleProperties rule = NetUtils.ipRewriteProperties.getRule().stream()
+ .filter(v -> NetUtils.subnetIp(v.getNetwork(), clientIp))
+ .findFirst()
+ .orElse(null);
+ if(rule == null) {
+ return sourceIp;
+ }
+ if(StringUtils.isNotEmpty(rule.getTargetHost())) {
+ return rule.getTargetHost();
+ }
+ final byte[] sourceBytes = sourceAddress.getAddress();
+ final byte[] clientBytes = clientAddress.getAddress();
+ final int length = (sourceBytes.length & clientBytes.length) * Byte.SIZE;
+ final BitSet sourceBit = BitSet.valueOf(sourceBytes);
+ final BitSet clientBit = BitSet.valueOf(clientBytes);
+ // 替换网络号保留主机号
+ for (int index = 0; index < NetUtils.ipRewriteProperties.getPrefix(); index++) {
+ sourceBit.set(index, clientBit.get(index));
+ }
+ final byte[] bytes = sourceBit.toByteArray();
+ if(bytes.length < length) {
+ final byte[] fillBytes = new byte[length / Byte.SIZE];
+ System.arraycopy(bytes, 0, fillBytes, 0, bytes.length);
+ return InetAddress.getByAddress(fillBytes).getHostAddress();
+ } else {
+ return InetAddress.getByAddress(bytes).getHostAddress();
+ }
+ }
+ } catch (UnknownHostException e) {
+ log.error("IP地址转换异常:{}-{}", sourceIp, clientIp, e);
+ }
+ return sourceIp;
+ }
+
+ /**
+ * @param ip IP
+ *
+ * @return 真实地址
+ *
+ * @throws UnknownHostException 地址转换异常
+ */
+ public static final InetAddress realAddress(String ip) throws UnknownHostException {
+ final InetAddress address = InetAddress.getByName(ip);
+ if(NetUtils.localAnyOrLoopAddress(address)) {
+ // 通配地址或者换回地址使用本机地址
+ return InetAddress.getByName(NetUtils.localIp);
+ }
+ return address;
+ }
+
+ /**
+ * @param inetAddress IP地址
+ *
+ * @return 通配地址或者换回地址
+ */
+ public static final boolean localAnyOrLoopAddress(InetAddress inetAddress) {
+ return
+ // 通配地址:0.0.0.0
+ inetAddress.isAnyLocalAddress() ||
+ // 环回地址:127.0.0.1 | 0:0:0:0:0:0:0:1
+ inetAddress.isLoopbackAddress();
+ }
+
+ /**
+ * @param inetAddress IP地址
+ *
+ * @return 本地IP
+ */
+ public static final boolean localAddress(InetAddress inetAddress) {
+ return
+ // 通配地址:0.0.0.0
+ inetAddress.isAnyLocalAddress() ||
+ // 环回地址:127.0.0.1 | 0:0:0:0:0:0:0:1
+ inetAddress.isLoopbackAddress() ||
+ // 链接地址:虚拟网卡
+ inetAddress.isLinkLocalAddress() ||
+ // 组播地址
+ inetAddress.isMulticastAddress() ||
+ // 本地地址:A/B/C类本地地址
+ inetAddress.isSiteLocalAddress();
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-boot/src/main/resources/banner.txt b/taoyao-signal-server/taoyao-boot/src/main/resources/banner.txt
index f62737c..bfc6249 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/resources/banner.txt
+++ b/taoyao-signal-server/taoyao-boot/src/main/resources/banner.txt
@@ -9,5 +9,5 @@
:: Spring Boot : ${spring-boot.formatted-version}
:: ${spring.application.name} : https://gitee.com/acgist/taoyao
-中庭地白树栖鸦,冷露无声湿桂花。
-今夜月明人尽望,不知秋思落谁家。
+去年今日此门中,人面桃花相映红。
+人面不知何处去,桃花依旧笑春风。
diff --git a/taoyao-signal-server/taoyao-boot/src/main/resources/logback-spring.xml b/taoyao-signal-server/taoyao-boot/src/main/resources/logback-spring.xml
index a040590..818eb81 100644
--- a/taoyao-signal-server/taoyao-boot/src/main/resources/logback-spring.xml
+++ b/taoyao-signal-server/taoyao-boot/src/main/resources/logback-spring.xml
@@ -4,6 +4,7 @@
${log.name}
+
@@ -11,6 +12,16 @@
+
+
+ ${log.charset}
+ ${log.pattern}
+
+
+ INFO
+
+
+
${log.path}/${log.name}.debug.log
@@ -79,35 +90,16 @@
true
0
-
-
-
- ${log.charset}
- ${log.pattern}
-
-
- INFO
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
diff --git a/taoyao-signal-server/taoyao-boot/src/test/java/com/acgist/taoyao/boot/utils/NetUtilsTest.java b/taoyao-signal-server/taoyao-boot/src/test/java/com/acgist/taoyao/boot/utils/NetUtilsTest.java
new file mode 100644
index 0000000..d003cd4
--- /dev/null
+++ b/taoyao-signal-server/taoyao-boot/src/test/java/com/acgist/taoyao/boot/utils/NetUtilsTest.java
@@ -0,0 +1,72 @@
+package com.acgist.taoyao.boot.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import com.acgist.taoyao.boot.config.IpRewriteProperties;
+import com.acgist.taoyao.boot.config.IpRewriteRuleProperties;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class NetUtilsTest {
+
+ private void init() {
+ final IpRewriteRuleProperties ipRewriteRuleProperties1 = new IpRewriteRuleProperties();
+ ipRewriteRuleProperties1.setNetwork("192.168.1.0");
+ final IpRewriteRuleProperties ipRewriteRuleProperties10 = new IpRewriteRuleProperties();
+ ipRewriteRuleProperties10.setNetwork("192.168.10.0");
+ final IpRewriteProperties ipRewriteProperties = new IpRewriteProperties();
+ ipRewriteProperties.setEnabled(true);
+ ipRewriteProperties.setPrefix(24);
+ ipRewriteProperties.setRule(List.of(ipRewriteRuleProperties1, ipRewriteRuleProperties10));
+ NetUtils.init(ipRewriteProperties);
+ }
+
+ @Test
+ public void testSubnetIp() {
+ this.init();
+ assertTrue(NetUtils.subnetIp("192.168.8.1", "192.168.8.100"));
+ assertTrue(NetUtils.subnetIp("192.168.100.1", "192.168.100.100"));
+ assertFalse(NetUtils.subnetIp("192.168.1.1", "192.168.8.100"));
+ assertFalse(NetUtils.subnetIp("192.168.80.1", "192.168.8.100"));
+ assertTrue(NetUtils.subnetIp("fe80::9ff9:2da9:9759:17e9", "fe80::9ff9:2da9:9759:17e9"));
+ assertTrue(NetUtils.subnetIp("fe80::9ff9:2da9:9759:17ee", "fe80::9ff9:2da9:9759:17e9"));
+ assertFalse(NetUtils.subnetIp("fe81::9ff9:2da9:9759:17e9", "fe80::9ff9:2da9:9759:17e9"));
+ assertFalse(NetUtils.subnetIp("fe81::9ff9:2da9:9759:17ee", "fe80::9ff9:2da9:9759:17e9"));
+ }
+
+ @Test
+ public void testRewriteIp() {
+ this.init();
+ assertNotEquals("192.168.1.0", NetUtils.rewriteIp("0.0.0.0", "192.168.1.1"));
+ assertEquals("192.168.1.100", NetUtils.rewriteIp("192.168.8.100", "192.168.1.1"));
+ assertEquals("192.168.10.100", NetUtils.rewriteIp("192.168.8.100", "192.168.10.1"));
+ }
+
+ @Test
+ public void testCost() {
+ this.init();
+ long a = System.currentTimeMillis();
+ for (int index = 0; index < 100000; index++) {
+ assertTrue(NetUtils.subnetIp("192.168.100.1", "192.168.100.100"));
+ assertFalse(NetUtils.subnetIp("192.168.1.1", "192.168.8.100"));
+ }
+ long z = System.currentTimeMillis();
+ log.info("耗时:{}", z - a);
+ a = System.currentTimeMillis();
+ for (int index = 0; index < 100000; index++) {
+ assertEquals("192.168.1.100", NetUtils.rewriteIp("192.168.8.100", "192.168.1.1"));
+ assertEquals("192.168.10.100", NetUtils.rewriteIp("192.168.8.100", "192.168.10.1"));
+ }
+ z = System.currentTimeMillis();
+ log.info("耗时:{}", z - a);
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-server/pom.xml b/taoyao-signal-server/taoyao-server/pom.xml
index d4851c6..689d972 100644
--- a/taoyao-signal-server/taoyao-server/pom.xml
+++ b/taoyao-signal-server/taoyao-server/pom.xml
@@ -26,6 +26,10 @@
com.acgist
taoyao-signal
+
+ org.hibernate.validator
+ hibernate-validator
+
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/configuration/TaoyaoAutoConfiguration.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/configuration/TaoyaoAutoConfiguration.java
index 7e56e07..701287d 100644
--- a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/configuration/TaoyaoAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/configuration/TaoyaoAutoConfiguration.java
@@ -1,13 +1,17 @@
package com.acgist.taoyao.configuration;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
+import com.acgist.taoyao.boot.config.SecurityProperties;
+import com.acgist.taoyao.boot.config.TaoyaoProperties;
import com.acgist.taoyao.interceptor.SecurityInterceptor;
import com.acgist.taoyao.interceptor.SlowInterceptor;
import com.acgist.taoyao.signal.service.SecurityService;
+import com.acgist.taoyao.signal.service.UsernamePasswordService;
import com.acgist.taoyao.signal.service.impl.SecurityServiceImpl;
/**
@@ -15,26 +19,29 @@ import com.acgist.taoyao.signal.service.impl.SecurityServiceImpl;
*
* @author acgist
*/
-@Configuration
+@AutoConfiguration
public class TaoyaoAutoConfiguration {
@Bean
@ConditionalOnMissingBean
- public SlowInterceptor slowInterceptor() {
- return new SlowInterceptor();
+ public SlowInterceptor slowInterceptor(TaoyaoProperties taoyaoProperties) {
+ return new SlowInterceptor(taoyaoProperties);
}
@Bean
@ConditionalOnMissingBean
- public SecurityService securityService() {
- return new SecurityServiceImpl();
+ public SecurityService securityService(
+ SecurityProperties securityProperties,
+ @Autowired(required = false) UsernamePasswordService usernamePasswordService
+ ) {
+ return new SecurityServiceImpl(securityProperties, usernamePasswordService);
}
@Bean
@ConditionalOnProperty(prefix = "taoyao.security", name = "enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnMissingBean
- public SecurityInterceptor securityInterceptor() {
- return new SecurityInterceptor();
+ public SecurityInterceptor securityInterceptor(SecurityService securityService, SecurityProperties securityProperties) {
+ return new SecurityInterceptor(securityService, securityProperties);
}
}
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ConfigController.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ConfigController.java
index 0aeea3c..f3dcd95 100644
--- a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ConfigController.java
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ConfigController.java
@@ -1,6 +1,5 @@
package com.acgist.taoyao.controller;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -24,13 +23,16 @@ import io.swagger.v3.oas.annotations.tags.Tag;
@RestController
@RequestMapping("/config")
public class ConfigController {
-
- @Autowired
- private MediaProperties mediaProperties;
- @Autowired
- private WebrtcProperties webrtcProperties;
+
+ private final MediaProperties mediaProperties;
+ private final WebrtcProperties webrtcProperties;
- @Operation(summary = "媒体配置", description = "媒体配置")
+ public ConfigController(MediaProperties mediaProperties, WebrtcProperties webrtcProperties) {
+ this.mediaProperties = mediaProperties;
+ this.webrtcProperties = webrtcProperties;
+ }
+
+ @Operation(summary = "媒体配置", description = "媒体配置")
@GetMapping("/media")
@ApiResponse(content = @Content(schema = @Schema(implementation = MediaProperties.class)))
public Message media() {
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ControlController.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ControlController.java
index e147564..52dbda9 100644
--- a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ControlController.java
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/controller/ControlController.java
@@ -1,14 +1,16 @@
package com.acgist.taoyao.controller;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.protocol.media.MediaRebootProtocol;
-import com.acgist.taoyao.signal.protocol.media.MediaShutdownProtocol;
+import com.acgist.taoyao.signal.protocol.control.ControlBellProtocol;
+import com.acgist.taoyao.signal.protocol.control.ControlPhotographProtocol;
+import com.acgist.taoyao.signal.protocol.control.ControlPtzProtocol;
+import com.acgist.taoyao.signal.protocol.control.ControlRecordProtocol;
import com.acgist.taoyao.signal.protocol.platform.PlatformRebootProtocol;
import com.acgist.taoyao.signal.protocol.platform.PlatformShutdownProtocol;
import com.acgist.taoyao.signal.protocol.system.SystemRebootProtocol;
@@ -16,6 +18,7 @@ import com.acgist.taoyao.signal.protocol.system.SystemShutdownProtocol;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.constraints.NotNull;
/**
* 控制
@@ -23,37 +26,68 @@ import io.swagger.v3.oas.annotations.tags.Tag;
* @author acgist
*/
@Tag(name = "控制", description = "控制管理")
+@Validated
@RestController
@RequestMapping("/control")
public class ControlController {
- @Autowired
- private MediaRebootProtocol mediaRebootProtocol;
- @Autowired
- private MediaShutdownProtocol mediaShutdownProtocol;
- @Autowired
- private SystemRebootProtocol systemRebootProtocol;
- @Autowired
- private SystemShutdownProtocol systemShutdownProtocol;
- @Autowired
- private PlatformRebootProtocol platformRebootProtocol;
- @Autowired
- private PlatformShutdownProtocol platformShutdownProtocol;
+ private final ControlPtzProtocol controlPtzProtocol;
+ private final ControlBellProtocol controlBellProtocol;
+ private final SystemRebootProtocol systemRebootProtocol;
+ private final ControlRecordProtocol controlRecordProtocol;
+ private final SystemShutdownProtocol systemShutdownProtocol;
+ private final PlatformRebootProtocol platformRebootProtocol;
+ private final PlatformShutdownProtocol platformShutdownProtocol;
+ private final ControlPhotographProtocol controlPhotographProtocol;
- @Operation(summary = "重启媒体", description = "重启媒体")
- @GetMapping("/media/reboot/{mediaId}")
- public Message mediaReboot(@PathVariable String mediaId) {
- this.mediaRebootProtocol.execute(mediaId);
- return Message.success();
- }
-
- @Operation(summary = "关闭媒体", description = "关闭媒体")
- @GetMapping("/media/shutdown/{mediaId}")
- public Message mediaShutdown(@PathVariable String mediaId) {
- this.mediaShutdownProtocol.execute(mediaId);
- return Message.success();
+ public ControlController(
+ ControlPtzProtocol controlPtzProtocol, ControlBellProtocol controlBellProtocol,
+ SystemRebootProtocol systemRebootProtocol, ControlRecordProtocol controlRecordProtocol,
+ SystemShutdownProtocol systemShutdownProtocol, PlatformRebootProtocol platformRebootProtocol,
+ PlatformShutdownProtocol platformShutdownProtocol, ControlPhotographProtocol controlPhotographProtocol
+ ) {
+ this.controlPtzProtocol = controlPtzProtocol;
+ this.controlBellProtocol = controlBellProtocol;
+ this.systemRebootProtocol = systemRebootProtocol;
+ this.controlRecordProtocol = controlRecordProtocol;
+ this.systemShutdownProtocol = systemShutdownProtocol;
+ this.platformRebootProtocol = platformRebootProtocol;
+ this.platformShutdownProtocol = platformShutdownProtocol;
+ this.controlPhotographProtocol = controlPhotographProtocol;
}
+ @Operation(summary = "PTZ", description = "PTZ")
+ @GetMapping("/ptz/{clientId}")
+ public Message ptz(
+ @NotNull(message = "PTZ类型不能为空") ControlPtzProtocol.Type type,
+ @NotNull(message = "PTZ参数不能为空") Double value,
+ @PathVariable String clientId
+ ) {
+ this.controlPtzProtocol.execute(type, value, clientId);
+ return Message.success();
+ }
+
+ @Operation(summary = "响铃", description = "响铃")
+ @GetMapping("/bell/{clientId}")
+ public Message bell(@PathVariable String clientId) {
+ this.controlBellProtocol.execute(clientId);
+ return Message.success();
+ }
+
+ @Operation(summary = "录像", description = "录像")
+ @GetMapping("/record/{clientId}")
+ public Message record(@PathVariable String clientId) {
+ this.controlRecordProtocol.execute(clientId);
+ return Message.success();
+ }
+
+ @Operation(summary = "拍照", description = "拍照")
+ @GetMapping("/photograph/{clientId}")
+ public Message photograph(@PathVariable String clientId) {
+ this.controlPhotographProtocol.execute(clientId);
+ return Message.success();
+ }
+
@Operation(summary = "重启系统", description = "重启系统")
@GetMapping("/system/reboot")
public Message systemReboot() {
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SecurityInterceptor.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SecurityInterceptor.java
index 2f764b4..c13201f 100644
--- a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SecurityInterceptor.java
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SecurityInterceptor.java
@@ -4,7 +4,6 @@ import java.util.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.util.AntPathMatcher;
@@ -25,12 +24,16 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SecurityInterceptor extends InterceptorAdapter {
- @Autowired
- private SecurityService securityService;
- @Autowired
- private SecurityProperties securityProperties;
+ private final SecurityService securityService;
+ private final SecurityProperties securityProperties;
+
+ public SecurityInterceptor(SecurityService securityService, SecurityProperties securityProperties) {
+ this.securityService = securityService;
+ this.securityProperties = securityProperties;
+ log.info("安全拦截密码:{}", securityProperties.getPassword());
+ }
- /**
+ /**
* 地址匹配
*/
private final AntPathMatcher matcher = new AntPathMatcher();
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SlowInterceptor.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SlowInterceptor.java
index adcf34b..24832f7 100644
--- a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SlowInterceptor.java
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/interceptor/SlowInterceptor.java
@@ -1,7 +1,5 @@
package com.acgist.taoyao.interceptor;
-import org.springframework.beans.factory.annotation.Autowired;
-
import com.acgist.taoyao.boot.config.TaoyaoProperties;
import com.acgist.taoyao.boot.interceptor.InterceptorAdapter;
@@ -17,10 +15,13 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SlowInterceptor extends InterceptorAdapter {
- @Autowired
- private TaoyaoProperties taoyaoProperties;
+ private final TaoyaoProperties taoyaoProperties;
- /**
+ public SlowInterceptor(TaoyaoProperties taoyaoProperties) {
+ this.taoyaoProperties = taoyaoProperties;
+ }
+
+ /**
* 请求开始时间
*/
private final ThreadLocal local = new ThreadLocal<>();
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/HTTPClientListener.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/QnjrcmzListener.java
similarity index 59%
rename from taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/HTTPClientListener.java
rename to taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/QnjrcmzListener.java
index 2f0f1bf..56d796f 100644
--- a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/HTTPClientListener.java
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/QnjrcmzListener.java
@@ -3,18 +3,24 @@ package com.acgist.taoyao.listener;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationListener;
+import lombok.extern.slf4j.Slf4j;
+
/**
- * 配置JDK HTTPClient域名校验问题
+ * 去年今日此门中
+ * 人面桃花相映红
*
* 注意:SpringApplicationRunListeners里面同步执行
*
* @author acgist
*/
-public class HTTPClientListener implements ApplicationListener {
+@Slf4j
+public class QnjrcmzListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
- synchronized (HTTPClientListener.class) {
+ synchronized (QnjrcmzListener.class) {
+ log.debug("配置忽略证书域名校验");
+ // 配置JDK HTTPClient域名校验问题
System.getProperties().setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
}
}
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/ThyjxcfListener.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/ThyjxcfListener.java
new file mode 100644
index 0000000..334571a
--- /dev/null
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/listener/ThyjxcfListener.java
@@ -0,0 +1,29 @@
+package com.acgist.taoyao.listener;
+
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.ApplicationListener;
+
+import com.acgist.taoyao.boot.annotation.Listener;
+import com.acgist.taoyao.boot.config.TaoyaoProperties;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 人面不知何处去
+ * 桃花依旧笑春风
+ *
+ * @author acgist
+ */
+@Slf4j
+@Listener
+public class ThyjxcfListener implements ApplicationListener {
+
+ @Override
+ public void onApplicationEvent(ApplicationReadyEvent event) {
+ synchronized (ThyjxcfListener.class) {
+ final TaoyaoProperties taoyaoProperties = event.getApplicationContext().getBean(TaoyaoProperties.class);
+ log.info("项目启动成功:{}", taoyaoProperties.getName());
+ }
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/main/TaoyaoApplication.java b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/main/TaoyaoApplication.java
index 80f28f7..f10f26a 100644
--- a/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/main/TaoyaoApplication.java
+++ b/taoyao-signal-server/taoyao-server/src/main/java/com/acgist/taoyao/main/TaoyaoApplication.java
@@ -10,6 +10,7 @@ public class TaoyaoApplication {
public static void main(String[] args) {
SpringApplication.run(TaoyaoApplication.class, args);
+// System.exit(SpringApplication.exit(SpringApplication.run(TaoyaoApplication.class, args)));
}
}
diff --git a/taoyao-signal-server/taoyao-server/src/main/resources/META-INF/spring.factories b/taoyao-signal-server/taoyao-server/src/main/resources/META-INF/spring.factories
index f4bc38e..5460a92 100644
--- a/taoyao-signal-server/taoyao-server/src/main/resources/META-INF/spring.factories
+++ b/taoyao-signal-server/taoyao-server/src/main/resources/META-INF/spring.factories
@@ -1 +1 @@
-org.springframework.context.ApplicationListener=com.acgist.taoyao.listener.HTTPClientListener
\ No newline at end of file
+org.springframework.context.ApplicationListener=com.acgist.taoyao.listener.QnjrcmzListener
diff --git a/taoyao-signal-server/taoyao-server/src/main/resources/application-dev.yml b/taoyao-signal-server/taoyao-server/src/main/resources/application-dev.yml
index e7e611f..af7073d 100644
--- a/taoyao-signal-server/taoyao-server/src/main/resources/application-dev.yml
+++ b/taoyao-signal-server/taoyao-server/src/main/resources/application-dev.yml
@@ -1,14 +1,10 @@
taoyao:
- media:
- media-server-list:
- - media-id: media-local-a
- enabled: false
- rewrite-ip: true
-# host: 192.168.1.110
- host: 192.168.8.110
- port: 9443
- schema: wss
- username: taoyao
- password: taoyao
security:
- permit: /v3/api-docs/**,/swagger-ui/**,/swagger-ui.html,/,/error,/index.html,/favicon.ico
\ No newline at end of file
+ permit:
+ - /
+ - /error
+ - /index.html
+ - /favicon.ico
+ - /v3/api-docs/**
+ - /swagger-ui/**
+ - /swagger-ui.html
\ No newline at end of file
diff --git a/taoyao-signal-server/taoyao-server/src/main/resources/application-prd.yml b/taoyao-signal-server/taoyao-server/src/main/resources/application-prd.yml
index c0d2200..9e5c416 100644
--- a/taoyao-signal-server/taoyao-server/src/main/resources/application-prd.yml
+++ b/taoyao-signal-server/taoyao-server/src/main/resources/application-prd.yml
@@ -1,4 +1,6 @@
taoyao:
+ security:
+ password: ${random.uuid}
script:
enabled: false
\ No newline at end of file
diff --git a/taoyao-signal-server/taoyao-server/src/main/resources/application.yml b/taoyao-signal-server/taoyao-server/src/main/resources/application.yml
index 842f409..61a07f3 100644
--- a/taoyao-signal-server/taoyao-server/src/main/resources/application.yml
+++ b/taoyao-signal-server/taoyao-server/src/main/resources/application.yml
@@ -19,6 +19,8 @@ server:
# servlet:
# context-path: /taoyao
spring:
+# main:
+# lazy-initialization: true
profiles:
active: dev
application:
@@ -51,7 +53,6 @@ taoyao:
name: 桃夭信令服务
version: 1.0.0
description: 桃夭WebRTC信令服务
- ip-mask: 24
timeout: 5000
id:
index: 0
@@ -99,24 +100,6 @@ taoyao:
bitrate: 800
frame-rate: 16
resolution: 720*480
- # 媒体服务配置
- media-server-list:
- - media-id: media-local-a
- enabled: true
- rewrite-ip: true
- host: 127.0.0.1
- port: 9443
- schema: wss
- username: taoyao
- password: taoyao
- - media-id: media-local-z
- enabled: true
- rewrite-ip: true
- host: 127.0.0.1
- port: 9443
- schema: wss
- username: taoyao
- password: taoyao
# Socket信令
socket:
enabled: true
@@ -151,18 +134,31 @@ taoyao:
security:
enabled: true
realm: taoyao
- permit: /,/error,/index.html,/favicon.ico
+ permit:
+ - /
+ - /error
+ - /index.html
+ - /favicon.ico
username: taoyao
password: taoyao
# 定时任务
scheduled:
media: 0 * * * * ?
client: 0 * * * * ?
+ # 地址重写
+ ip-rewrite:
+ enabled: true
+ prefix: 24
+ rule:
+ - prefix: 24
+ network: 192.168.1.0
+ target-host:
+ - prefix: 24
+ network: 192.168.8.0
+ target-host:
# 脚本配置
script:
enabled: true
- media-reboot: pm2 restart taoyao-client-media
- media-shutdown: pm2 stop taoyao-client-media
system-reboot: reboot
system-shutdown: shutdown now
platform-reboot: systemctl restart taoyao-signal-server
diff --git a/taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IdServiceTest.java b/taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IdServiceTest.java
index 789eba0..493c4f0 100644
--- a/taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IdServiceTest.java
+++ b/taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IdServiceTest.java
@@ -25,6 +25,7 @@ class IdServiceTest {
void testId() {
final long id = this.idService.buildId();
log.info("生成ID:{}", id);
+ log.info("生成ID:{}", String.valueOf(id).length());
}
@Test
diff --git a/taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IpServiceTest.java b/taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IpServiceTest.java
deleted file mode 100644
index 88f1614..0000000
--- a/taoyao-signal-server/taoyao-server/src/test/java/com/acgist/taoyao/service/IpServiceTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.acgist.taoyao.service;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.acgist.taoyao.annotation.TaoyaoTest;
-import com.acgist.taoyao.boot.service.IpService;
-import com.acgist.taoyao.main.TaoyaoApplication;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@TaoyaoTest(classes = TaoyaoApplication.class)
-public class IpServiceTest {
-
- @Autowired
- private IpService ipService;
-
- @Test
- public void testDomain() throws UnknownHostException {
- InetAddress byName = InetAddress.getByName("www.acgist.com");
- System.out.println(byName);
- System.out.println(byName.getHostAddress());
- System.out.println(byName.getHostName());
- System.out.println(byName.isAnyLocalAddress());
- System.out.println(byName.isLoopbackAddress());
- System.out.println(byName.isLinkLocalAddress());
- System.out.println(byName.isMulticastAddress());
- System.out.println(byName.isSiteLocalAddress());
- }
-
- @Test
- public void testSubnetIp() {
- assertTrue(this.ipService.subnetIp("192.168.8.1", "192.168.8.100"));
- assertTrue(this.ipService.subnetIp("192.168.100.1", "192.168.100.100"));
- assertFalse(this.ipService.subnetIp("192.168.1.1", "192.168.8.100"));
- assertFalse(this.ipService.subnetIp("192.168.80.1", "192.168.8.100"));
- assertTrue(this.ipService.subnetIp("fe80::9ff9:2da9:9759:17e9", "fe80::9ff9:2da9:9759:17e9"));
- assertTrue(this.ipService.subnetIp("fe80::9ff9:2da9:9759:17ee", "fe80::9ff9:2da9:9759:17e9"));
- assertFalse(this.ipService.subnetIp("fe81::9ff9:2da9:9759:17e9", "fe80::9ff9:2da9:9759:17e9"));
- assertFalse(this.ipService.subnetIp("fe81::9ff9:2da9:9759:17ee", "fe80::9ff9:2da9:9759:17e9"));
- }
-
- @Test
- public void testRewriteIp() {
- assertEquals("192.168.1.0", this.ipService.rewriteIp("0.0.0.0", "192.168.1.1"));
- assertEquals("192.168.1.10", this.ipService.rewriteIp("0.0.0.0", "192.168.1.1", "192.168.8.10"));
- assertEquals("192.168.1.100", this.ipService.rewriteIp("192.168.8.100", "192.168.1.1"));
- assertEquals("192.168.10.100", this.ipService.rewriteIp("192.168.8.100", "192.168.10.1"));
- }
-
- @Test
- public void testCost() {
- long a = System.currentTimeMillis();
- for (int index = 0; index < 100000; index++) {
- assertTrue(this.ipService.subnetIp("192.168.100.1", "192.168.100.100"));
- assertFalse(this.ipService.subnetIp("192.168.1.1", "192.168.8.100"));
- }
- long z = System.currentTimeMillis();
- log.info("耗时:{}", z - a);
- a = System.currentTimeMillis();
- for (int index = 0; index < 100000; index++) {
- assertEquals("192.168.1.100", this.ipService.rewriteIp("192.168.8.100", "192.168.1.1"));
- assertEquals("192.168.10.100", this.ipService.rewriteIp("192.168.8.100", "192.168.10.1"));
- }
- z = System.currentTimeMillis();
- log.info("耗时:{}", z - a);
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/MapBodyGetter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/MapBodyGetter.java
deleted file mode 100644
index db44b21..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/MapBodyGetter.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.acgist.taoyao.signal;
-
-import java.util.Map;
-
-/**
- * Map参数Getter
- *
- * @author acgist
- */
-public interface MapBodyGetter {
-
- /**
- * @param 参数泛型
- *
- * @param body 消息主体
- * @param key 参数名称
- *
- * @return 参数值
- */
- @SuppressWarnings("unchecked")
- default T get(Map, ?> body, String key) {
- if(body == null) {
- return null;
- }
- return (T) body.get(key);
- }
-
- /**
- * @param 参数泛型
- *
- * @param body 消息主体
- * @param key 参数名称
- * @param defaultValue 参数默认值
- *
- * @return 参数值
- */
- @SuppressWarnings("unchecked")
- default T get(Map, ?> body, String key, T defaultValue) {
- if(body == null) {
- return defaultValue;
- }
- final T t = (T) body.get(key);
- return t == null ? defaultValue : t;
- }
-
- /**
- * @param body 消息主体
- * @param key 参数名称
- *
- * @return 参数值
- */
- default Long getLong(Map, ?> body, String key) {
- if(body == null) {
- return null;
- }
- final Object object = body.get(key);
- if(object == null) {
- return null;
- } else if(object instanceof Long value) {
- return value;
- }
- return Long.valueOf(object.toString());
- }
-
- /**
- * @param body 消息主体
- * @param key 参数名称
- *
- * @return 参数值
- */
- default Integer getInteger(Map, ?> body, String key) {
- if(body == null) {
- return null;
- }
- final Object object = body.get(key);
- if(object == null) {
- return null;
- } else if(object instanceof Integer value) {
- return value;
- }
- return Integer.valueOf(object.toString());
- }
-
- /**
- * @param body 消息主体
- * @param key 参数名称
- *
- * @return 参数值
- */
- default Boolean getBoolean(Map, ?> body, String key) {
- if(body == null) {
- return null;
- }
- final Object object = body.get(key);
- if(object == null) {
- return null;
- } else if(object instanceof Boolean value) {
- return value;
- }
- return Boolean.valueOf(object.toString());
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/Client.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/Client.java
index c9bc080..44ee177 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/Client.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/Client.java
@@ -1,8 +1,6 @@
package com.acgist.taoyao.signal.client;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.media.Peer;
/**
* 终端
@@ -22,9 +20,9 @@ public interface Client extends AutoCloseable {
String clientId();
/**
- * @return Peer
+ * @return 终端类型
*/
- Peer peer();
+ ClientType clientType();
/**
* @return 终端状态
@@ -38,6 +36,25 @@ public interface Client extends AutoCloseable {
*/
void push(Message message);
+ /**
+ * 请求消息
+ *
+ * @param request 消息
+ *
+ * @return 响应
+ */
+ Message request(Message request);
+
+ /**
+ * 响应消息
+ *
+ * @param id 消息标识
+ * @param message 消息
+ *
+ * @return 是否响应消息
+ */
+ boolean response(Long id, Message message);
+
/**
* @param timeout 超时时间
*
@@ -62,14 +79,4 @@ public interface Client extends AutoCloseable {
*/
boolean authorized();
- /**
- * @return 媒体服务终端
- */
- MediaClient mediaClient();
-
- /**
- * @param mediaClient 媒体服务终端
- */
- void mediaClient(MediaClient mediaClient);
-
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientAdapter.java
index 6aeb6fa..53052a7 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientAdapter.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientAdapter.java
@@ -1,7 +1,14 @@
package com.acgist.taoyao.signal.client;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.media.Peer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.acgist.taoyao.boot.model.Header;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.model.MessageCode;
+import com.acgist.taoyao.boot.model.MessageCodeException;
+
+import lombok.extern.slf4j.Slf4j;
/**
* 终端适配器
@@ -10,6 +17,7 @@ import com.acgist.taoyao.signal.media.Peer;
*
* @param 实例泛型
*/
+@Slf4j
public abstract class ClientAdapter implements Client {
/**
@@ -21,6 +29,10 @@ public abstract class ClientAdapter implements Client {
*/
protected final long time;
/**
+ * 超时时间
+ */
+ protected final long timeout;
+ /**
* 终端标识
*/
protected String clientId;
@@ -32,25 +44,22 @@ public abstract class ClientAdapter implements Client {
* 是否授权
*/
protected boolean authorized;
- /**
- * Peer
- */
- protected Peer peer;
/**
* 终端状态
*/
- protected ClientStatus status;
+ protected final ClientStatus status;
/**
- * 媒体服务终端
- */
- protected MediaClient mediaClient;
+ * 同步消息
+ */
+ protected final Map requestMessage;
- protected ClientAdapter(T instance) {
+ protected ClientAdapter(long timeout, T instance) {
this.time = System.currentTimeMillis();
+ this.timeout = timeout;
this.instance = instance;
this.authorized = false;
- this.peer = new Peer(this);
this.status = new ClientStatus();
+ this.requestMessage = new ConcurrentHashMap<>();
}
@Override
@@ -64,8 +73,8 @@ public abstract class ClientAdapter implements Client {
}
@Override
- public Peer peer() {
- return this.peer;
+ public ClientType clientType() {
+ return this.status.getClientType();
}
@Override
@@ -73,6 +82,43 @@ public abstract class ClientAdapter implements Client {
return this.status;
}
+ @Override
+ public Message request(Message request) {
+ final Header header = request.getHeader();
+ final Long id = header.getId();
+ this.requestMessage.put(id, request);
+ synchronized (request) {
+ this.push(request);
+ try {
+ request.wait(this.timeout);
+ } catch (InterruptedException e) {
+ log.error("媒体服务等待响应异常:{}", request, e);
+ }
+ }
+ final Message response = this.requestMessage.remove(id);
+ if (response == null || request.equals(response)) {
+ log.warn("媒体服务没有响应:{}", request);
+ throw MessageCodeException.of(MessageCode.CODE_2001, "媒体服务没有响应");
+ }
+ return response;
+ }
+
+ @Override
+ public boolean response(Long id, Message message) {
+ final Message request = this.requestMessage.get(id);
+ if (request != null) {
+ // 同步处理:重新设置响应消息
+ this.requestMessage.put(id, message);
+ // 唤醒等待线程
+ synchronized (request) {
+ request.notifyAll();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
@Override
public boolean timeout(long timeout) {
return System.currentTimeMillis() - this.time > timeout;
@@ -94,17 +140,6 @@ public abstract class ClientAdapter implements Client {
return this.authorized;
}
- @Override
- public MediaClient mediaClient() {
- return this.mediaClient;
- }
-
- @Override
- public void mediaClient(MediaClient mediaClient) {
- this.mediaClient = mediaClient;
- this.status.setMediaId(mediaClient.mediaId());
- }
-
@Override
public void close() throws Exception {
this.instance.close();
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientManager.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientManager.java
index 18f0740..634e61a 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientManager.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientManager.java
@@ -4,14 +4,14 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.commons.lang3.ArrayUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import com.acgist.taoyao.boot.annotation.Manager;
import com.acgist.taoyao.boot.config.TaoyaoProperties;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.event.client.ClientCloseEvent;
+import com.acgist.taoyao.signal.event.ClientCloseEvent;
import lombok.extern.slf4j.Slf4j;
@@ -24,19 +24,23 @@ import lombok.extern.slf4j.Slf4j;
@Manager
public class ClientManager {
- @Autowired
- private TaoyaoProperties taoyaoProperties;
- @Autowired
- private ApplicationContext applicationContext;
+ private final TaoyaoProperties taoyaoProperties;
+ private final ApplicationContext applicationContext;
/**
* 终端列表
*/
- private List clients = new CopyOnWriteArrayList<>();
-
+ private final List clients;
+
+ public ClientManager(TaoyaoProperties taoyaoProperties, ApplicationContext applicationContext) {
+ this.taoyaoProperties = taoyaoProperties;
+ this.applicationContext = applicationContext;
+ this.clients = new CopyOnWriteArrayList<>();
+ }
+
@Scheduled(cron = "${taoyao.scheduled.client:0 * * * * ?}")
public void scheduled() {
- this.closeTimeout();
+ this.closeTimeout();
}
/**
@@ -75,12 +79,11 @@ public class ClientManager {
/**
* 授权终端广播消息
*
- * TODO:类型
- *
* @param message 消息
+ * @param clientTypes 终端类型
*/
- public void broadcast(Message message) {
- this.clients().forEach(v -> v.push(message));
+ public void broadcast(Message message, ClientType ... clientTypes) {
+ this.clients(clientTypes).forEach(v -> v.push(message));
}
/**
@@ -88,9 +91,10 @@ public class ClientManager {
*
* @param from 发送终端
* @param message 消息
+ * @param clientTypes 终端类型
*/
- public void broadcast(String from, Message message) {
- this.clients().stream()
+ public void broadcast(String from, Message message, ClientType ... clientTypes) {
+ this.clients(clientTypes).stream()
.filter(v -> !Objects.equals(from, v.clientId()))
.forEach(v -> v.push(message));
}
@@ -100,9 +104,10 @@ public class ClientManager {
*
* @param from 发送终端
* @param message 消息
+ * @param clientTypes 终端类型
*/
- public void broadcast(Client from, Message message) {
- this.clients().stream()
+ public void broadcast(Client from, Message message, ClientType ... clientTypes) {
+ this.clients(clientTypes).stream()
.filter(v -> v.instance() != from)
.forEach(v -> v.push(message));
}
@@ -112,7 +117,7 @@ public class ClientManager {
*
* @return 终端
*/
- public Client client(AutoCloseable instance) {
+ public Client clients(AutoCloseable instance) {
return this.clients.stream()
.filter(v -> v.instance() == instance)
.findFirst()
@@ -122,20 +127,24 @@ public class ClientManager {
/**
* @param clientId 终端标识
*
- * @return 授权终端列表
+ * @return 授权终端
*/
- public List clients(String clientId) {
+ public Client clients(String clientId) {
return this.clients().stream()
.filter(v -> Objects.equals(clientId, v.clientId()))
- .toList();
+ .findFirst()
+ .orElse(null);
}
/**
+ * @param clientTypes 终端类型
+ *
* @return 所有授权终端列表
*/
- public List clients() {
+ public List clients(ClientType ... clientTypes) {
return this.clients.stream()
.filter(Client::authorized)
+ .filter(client -> ArrayUtils.isEmpty(clientTypes) || ArrayUtils.contains(clientTypes, client.clientType()))
.toList();
}
@@ -145,26 +154,27 @@ public class ClientManager {
* @return 终端状态
*/
public ClientStatus status(AutoCloseable instance) {
- final Client client = this.client(instance);
+ final Client client = this.clients(instance);
return client == null ? null : client.status();
}
/**
* @param clientId 终端标识
*
- * @return 授权终端状态列表
+ * @return 授权终端状态
*/
- public List status(String clientId) {
- return this.clients(clientId).stream()
- .map(Client::status)
- .toList();
+ public ClientStatus status(String clientId) {
+ final Client client = this.clients(clientId);
+ return client == null ? null : client.status();
}
/**
+ * @param clientTypes 终端类型
+ *
* @return 所有授权终端状态列表
*/
- public List status() {
- return this.clients().stream()
+ public List status(ClientType ... clientTypes) {
+ return this.clients(clientTypes).stream()
.map(Client::status)
.toList();
}
@@ -176,7 +186,7 @@ public class ClientManager {
* @param message 消息
*/
public void push(AutoCloseable instance, Message message) {
- final Client client = this.client(instance);
+ final Client client = this.clients(instance);
if(client == null) {
log.warn("推送消息终端无效:{}-{}", instance, message);
return;
@@ -190,7 +200,7 @@ public class ClientManager {
* @param instance 终端实例
*/
public void close(AutoCloseable instance) {
- final Client client = this.client(instance);
+ final Client client = this.clients(instance);
try {
if(client != null) {
client.close();
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientStatus.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientStatus.java
index 5e0a5be..2db4d16 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientStatus.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientStatus.java
@@ -1,6 +1,11 @@
package com.acgist.taoyao.signal.client;
import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.acgist.taoyao.boot.config.Constant;
+import com.acgist.taoyao.boot.utils.MapUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
@@ -18,23 +23,79 @@ public class ClientStatus {
@Schema(title = "终端IP", description = "终端IP")
private String ip;
- @Schema(title = "媒体服务标识", description = "媒体服务标识")
- private String mediaId;
+ @Schema(title = "终端名称", description = "终端名称")
+ private String name;
@Schema(title = "终端标识", description = "终端标识")
private String clientId;
+ @Schema(title = "终端类型", description = "终端类型")
+ private ClientType clientType;
+ @Schema(title = "纬度", description = "纬度")
+ private Double latitude;
+ @Schema(title = "经度", description = "经度")
+ private Double longitude;
+ @Schema(title = "湿度", description = "湿度")
+ private Double humidity;
+ @Schema(title = "温度", description = "温度")
+ private Double temperature;
@Schema(title = "信号强度(0~100)", description = "信号强度(0~100)")
- private Integer signal = 0;
+ private Integer signal;
@Schema(title = "电池电量(0~100)", description = "电池电量(0~100)")
- private Integer battery = 0;
+ private Integer battery;
+ @Schema(title = "是否正在运行", description = "是否正在运行")
+ private Boolean running;
@Schema(title = "是否正在充电", description = "是否正在充电")
- private Boolean charging = false;
+ private Boolean charging;
@Schema(title = "是否正在录像", description = "是否正在录像")
- private Boolean recording = false;
- @Schema(title = "生产者数量", description = "生产者数量")
- private Integer producerSize;
- @Schema(title = "消费者数量", description = "消费者数量")
- private Integer customerSize;
+ private Boolean recording;
@Schema(title = "最后心跳时间", description = "最后心跳时间")
private LocalDateTime lastHeartbeat;
+ @Schema(title = "终端状态", description = "其他扩展终端状态")
+ private Map status = new HashMap<>();
+ @Schema(title = "终端配置", description = "其他扩展终端配置")
+ private Map config = new HashMap<>();
+
+ /**
+ * 拷贝属性
+ *
+ * @param body 消息主体
+ */
+ public void copy(Map body) {
+ this.setLatitude(MapUtils.get(body, Constant.LATITUDE));
+ this.setLongitude(MapUtils.get(body, Constant.LONGITUDE));
+ this.setHumidity(MapUtils.get(body, Constant.HUMIDITY));
+ this.setTemperature(MapUtils.get(body, Constant.TEMPERATURE));
+ this.setSignal(MapUtils.get(body, Constant.SIGNAL));
+ this.setBattery(MapUtils.get(body, Constant.BATTERY));
+ this.setRunning(MapUtils.get(body, Constant.RUNNING));
+ this.setCharging(MapUtils.get(body, Constant.CHARGING));
+ this.setRecording(MapUtils.get(body, Constant.RECORDING));
+ this.setLastHeartbeat(LocalDateTime.now());
+ this.status(MapUtils.get(body, Constant.STATUS));
+ this.config(MapUtils.get(body, Constant.CONFIG));
+ }
+
+ /**
+ * 拷贝状态
+ *
+ * @param map 状态
+ */
+ public void status(Map map) {
+ if(map == null) {
+ return;
+ }
+ map.forEach(this.status::put);
+ }
+
+ /**
+ * 拷贝配置
+ *
+ * @param map 配置
+ */
+ public void config(Map map) {
+ if(map == null) {
+ return;
+ }
+ map.forEach(this.config::put);
+ }
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientType.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientType.java
index 683030c..98fe0ad 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientType.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientType.java
@@ -1,45 +1,43 @@
package com.acgist.taoyao.signal.client;
+import com.acgist.taoyao.boot.model.MessageCodeException;
+
+import lombok.Getter;
+
/**
* 终端类型
*
* @author acgist
*/
+@Getter
public enum ClientType {
- /**
- * 前端页面
- */
- WEB,
- /**
- * 媒体服务
- */
- MEDIA,
- /**
- * 洗衣机
- */
- WASHER,
- /**
- * 摄像头
- */
- CAMERA,
- /**
- * 冰箱
- */
- REFRIGERATOR,
- /**
- * 电视机
- */
- TELEVISION,
- /**
- * 空调
- */
- AIR_CONDITIONER,
- /**
- * 热水器
- */
- WATER_HEATER;
+ WEB("Web"),
+ MEDIA("媒体服务"),
+ CAMERA("摄像头"),
+ OTHER("其他终端");
+ /**
+ * 名称
+ */
private final String name;
+
+ private ClientType(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @param value 类型
+ *
+ * @return 类型
+ */
+ public static final ClientType of(String value) {
+ for (ClientType type : ClientType.values()) {
+ if(type.name().equalsIgnoreCase(value)) {
+ return type;
+ }
+ }
+ throw MessageCodeException.of("未知终端类型:" + value);
+ }
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketClient.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketClient.java
index 611fea8..49c1fa9 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketClient.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketClient.java
@@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.model.MessageCodeException;
import com.acgist.taoyao.signal.client.ClientAdapter;
import lombok.Getter;
@@ -25,10 +26,6 @@ import lombok.extern.slf4j.Slf4j;
@Setter
public class SocketClient extends ClientAdapter {
- /**
- * 超时时间
- */
- private final long timeout;
/**
* 换行符号
*/
@@ -39,15 +36,10 @@ public class SocketClient extends ClientAdapter {
private final int lineLength;
public SocketClient(Long timeout, AsynchronousSocketChannel instance) {
- super(instance);
- this.timeout = timeout;
+ super(timeout, instance);
+ this.ip = this.clientIp(instance);
this.line = Constant.LINE.getBytes();
this.lineLength = this.line.length;
- try {
- this.ip = ((InetSocketAddress) instance.getRemoteAddress()).getHostString();
- } catch (IOException e) {
- log.error("Socket终端获取远程IP异常", e);
- }
}
@Override
@@ -71,4 +63,17 @@ public class SocketClient extends ClientAdapter {
}
}
+ /**
+ * @param instance 终端实例
+ *
+ * @return 终端IP
+ */
+ private String clientIp(AsynchronousSocketChannel instance) {
+ try {
+ return ((InetSocketAddress) instance.getRemoteAddress()).getHostString();
+ } catch (IOException e) {
+ throw MessageCodeException.of(e, "无效终端(IP):" + instance);
+ }
+ }
+
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketSignalMessageHandler.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketSignalMessageHandler.java
index 232c477..572bfee 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketSignalMessageHandler.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/socket/SocketSignalMessageHandler.java
@@ -71,6 +71,7 @@ public final class SocketSignalMessageHandler implements CompletionHandler {
- public WebSocketClient(Session instance) {
- super(instance);
- final Map userProperties = instance.getUserProperties();
- this.ip = (String) userProperties.get(Constant.IP);
+ public WebSocketClient(long timeout, Session instance) {
+ super(timeout, instance);
+ this.ip = (String) instance.getUserProperties().get(Constant.IP);
}
@Override
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignal.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignal.java
index f037752..7d69d0f 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignal.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignal.java
@@ -2,6 +2,7 @@ package com.acgist.taoyao.signal.client.websocket;
import org.springframework.beans.factory.annotation.Autowired;
+import com.acgist.taoyao.boot.config.TaoyaoProperties;
import com.acgist.taoyao.signal.client.ClientManager;
import com.acgist.taoyao.signal.protocol.ProtocolManager;
import com.acgist.taoyao.signal.protocol.platform.PlatformErrorProtocol;
@@ -25,12 +26,13 @@ public class WebSocketSignal {
private static ClientManager clientManager;
private static ProtocolManager protocolManager;
+ private static TaoyaoProperties taoyaoProperties;
private static PlatformErrorProtocol platformErrorProtocol;
@OnOpen
public void open(Session session) {
log.debug("WebSocket信令终端连接成功:{}", session);
- WebSocketSignal.clientManager.open(new WebSocketClient(session));
+ WebSocketSignal.clientManager.open(new WebSocketClient(WebSocketSignal.taoyaoProperties.getTimeout(), session));
}
@OnMessage
@@ -39,7 +41,7 @@ public class WebSocketSignal {
try {
WebSocketSignal.protocolManager.execute(message.strip(), session);
} catch (Exception e) {
- log.error("处理WebSocket信令消息异常:{}", message, e);
+ log.error("处理WebSocket信令消息异常:{}-{}", WebSocketSignal.clientManager.clients(session), message, e);
WebSocketSignal.clientManager.push(session, WebSocketSignal.platformErrorProtocol.build(e));
}
}
@@ -66,6 +68,11 @@ public class WebSocketSignal {
WebSocketSignal.protocolManager = protocolManager;
}
+ @Autowired
+ public void setTaoyaoProperties(TaoyaoProperties taoyaoProperties) {
+ WebSocketSignal.taoyaoProperties = taoyaoProperties;
+ }
+
@Autowired
public void setPlatformErrorProtocol(PlatformErrorProtocol platformErrorProtocol) {
WebSocketSignal.platformErrorProtocol = platformErrorProtocol;
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignalConfigurator.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignalConfigurator.java
index 65fab33..1da692c 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignalConfigurator.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/websocket/WebSocketSignalConfigurator.java
@@ -1,23 +1,21 @@
package com.acgist.taoyao.signal.client.websocket;
import java.lang.reflect.Field;
-import java.util.Map;
import org.apache.catalina.connector.RequestFacade;
import com.acgist.taoyao.boot.config.Constant;
+import com.acgist.taoyao.boot.model.MessageCodeException;
import jakarta.websocket.HandshakeResponse;
import jakarta.websocket.server.HandshakeRequest;
import jakarta.websocket.server.ServerEndpointConfig;
-import lombok.extern.slf4j.Slf4j;
/**
* WebSocket信令配置
*
* @author acgist
*/
-@Slf4j
public class WebSocketSignalConfigurator extends ServerEndpointConfig.Configurator {
@Override
@@ -25,11 +23,9 @@ public class WebSocketSignalConfigurator extends ServerEndpointConfig.Configurat
try {
final Field field = request.getClass().getDeclaredField(Constant.REQUEST);
field.setAccessible(true);
- final RequestFacade requestFacade = (RequestFacade) field.get(request);
- final Map userProperties = config.getUserProperties();
- userProperties.put(Constant.IP, requestFacade.getRemoteAddr());
+ config.getUserProperties().put(Constant.IP, ((RequestFacade) field.get(request)).getRemoteAddr());
} catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
- log.error("WebSocket终端获取远程IP异常", e);
+ throw MessageCodeException.of(e, "无效终端(IP):" + request);
}
super.modifyHandshake(config, request, response);
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/MediaClientAutoConfiguration.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/MediaClientAutoConfiguration.java
deleted file mode 100644
index 21c2d75..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/MediaClientAutoConfiguration.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.acgist.taoyao.signal.configuration;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import com.acgist.taoyao.boot.runner.OrderedCommandLineRunner;
-import com.acgist.taoyao.signal.media.MediaClientManager;
-
-/**
- * 媒体服务自动配置
- *
- * @author acgist
- */
-@Configuration
-public class MediaClientAutoConfiguration {
-
- @Bean
- @Autowired
- public CommandLineRunner mediaCommandLineRunner(MediaClientManager mediaClientManager) {
- return new OrderedCommandLineRunner() {
- @Override
- public void run(String ... args) throws Exception {
- mediaClientManager.init();
- }
- };
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/ScriptAutoConfiguration.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/ScriptAutoConfiguration.java
index 297d9ae..ea14ab3 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/ScriptAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/ScriptAutoConfiguration.java
@@ -1,12 +1,11 @@
package com.acgist.taoyao.signal.configuration;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import com.acgist.taoyao.signal.protocol.media.MediaRebootProtocol;
-import com.acgist.taoyao.signal.protocol.media.MediaShutdownProtocol;
+import com.acgist.taoyao.boot.config.ScriptProperties;
import com.acgist.taoyao.signal.protocol.platform.PlatformRebootProtocol;
import com.acgist.taoyao.signal.protocol.platform.PlatformScriptProtocol;
import com.acgist.taoyao.signal.protocol.platform.PlatformShutdownProtocol;
@@ -18,50 +17,38 @@ import com.acgist.taoyao.signal.protocol.system.SystemShutdownProtocol;
*
* @author acgist
*/
-@Configuration
+@AutoConfiguration
@ConditionalOnProperty(prefix = "taoyao.script", name = "enabled", havingValue = "true", matchIfMissing = true)
public class ScriptAutoConfiguration {
@Bean
@ConditionalOnMissingBean
- public MediaRebootProtocol mediaRebootProtocol() {
- return new MediaRebootProtocol();
+ public SystemRebootProtocol systemRebootProtocol(ScriptProperties scriptProperties) {
+ return new SystemRebootProtocol(scriptProperties);
}
@Bean
@ConditionalOnMissingBean
- public MediaShutdownProtocol mediaShutdownProtocol() {
- return new MediaShutdownProtocol();
+ public SystemShutdownProtocol systemShutdownProtocol(ScriptProperties scriptProperties) {
+ return new SystemShutdownProtocol(scriptProperties);
}
@Bean
@ConditionalOnMissingBean
- public SystemRebootProtocol systemRebootProtocol() {
- return new SystemRebootProtocol();
+ public PlatformRebootProtocol platformRebootProtocol(ScriptProperties scriptProperties) {
+ return new PlatformRebootProtocol(scriptProperties);
}
@Bean
@ConditionalOnMissingBean
- public SystemShutdownProtocol systemShutdownProtocol() {
- return new SystemShutdownProtocol();
+ public PlatformShutdownProtocol platformShutdownProtocol(ScriptProperties scriptProperties) {
+ return new PlatformShutdownProtocol(scriptProperties);
}
@Bean
@ConditionalOnMissingBean
public PlatformScriptProtocol platformScriptProtocol() {
- return new PlatformScriptProtocol();
- }
-
- @Bean
- @ConditionalOnMissingBean
- public PlatformRebootProtocol platformRebootProtocol() {
- return new PlatformRebootProtocol();
- }
-
- @Bean
- @ConditionalOnMissingBean
- public PlatformShutdownProtocol platformShutdownProtocol() {
- return new PlatformShutdownProtocol();
+ return new PlatformScriptProtocol();
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SignalAutoConfiguration.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SignalAutoConfiguration.java
index 6321134..459f6f1 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SignalAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SignalAutoConfiguration.java
@@ -1,9 +1,8 @@
package com.acgist.taoyao.signal.configuration;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
import com.acgist.taoyao.boot.runner.OrderedCommandLineRunner;
import com.acgist.taoyao.signal.protocol.ProtocolManager;
@@ -13,11 +12,10 @@ import com.acgist.taoyao.signal.protocol.ProtocolManager;
*
* @author acgist
*/
-@Configuration
+@AutoConfiguration
public class SignalAutoConfiguration {
@Bean
- @Autowired
public CommandLineRunner signalCommandLineRunner(ProtocolManager protocolManager) {
return new OrderedCommandLineRunner() {
@Override
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SocketSignalAutoConfiguration.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SocketSignalAutoConfiguration.java
index b1eb9cf..eb4afeb 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SocketSignalAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/SocketSignalAutoConfiguration.java
@@ -1,12 +1,11 @@
package com.acgist.taoyao.signal.configuration;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
import com.acgist.taoyao.boot.config.SocketProperties;
import com.acgist.taoyao.boot.runner.OrderedCommandLineRunner;
@@ -20,12 +19,11 @@ import com.acgist.taoyao.signal.protocol.platform.PlatformErrorProtocol;
*
* @author acgist
*/
-@Configuration
+@AutoConfiguration
@ConditionalOnProperty(prefix = "taoyao.socket", name = "enabled", havingValue = "true", matchIfMissing = true)
public class SocketSignalAutoConfiguration {
@Bean
- @Autowired
@ConditionalOnMissingBean
public SocketSignal socketSignal(
ClientManager clientManager,
@@ -37,7 +35,6 @@ public class SocketSignalAutoConfiguration {
}
@Bean
- @Autowired
@ConditionalOnBean(SocketSignal.class)
public CommandLineRunner socketSignalCommandLineRunner(SocketSignal socketSignal) {
return new OrderedCommandLineRunner() {
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/WebSocketSignalAutoConfiguration.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/WebSocketSignalAutoConfiguration.java
index eb8b86c..4a0b60b 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/WebSocketSignalAutoConfiguration.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/configuration/WebSocketSignalAutoConfiguration.java
@@ -1,8 +1,8 @@
package com.acgist.taoyao.signal.configuration;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@@ -13,8 +13,8 @@ import com.acgist.taoyao.signal.client.websocket.WebSocketSignal;
*
* @author acgist
*/
-@Configuration
@EnableWebSocket
+@AutoConfiguration
public class WebSocketSignalAutoConfiguration {
@Bean
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ClientController.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ClientController.java
index 14e2f87..f545987 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ClientController.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ClientController.java
@@ -1,6 +1,5 @@
package com.acgist.taoyao.signal.controller;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -11,6 +10,7 @@ import com.acgist.taoyao.signal.client.ClientManager;
import com.acgist.taoyao.signal.client.ClientStatus;
import com.acgist.taoyao.signal.protocol.client.ClientRebootProtocol;
import com.acgist.taoyao.signal.protocol.client.ClientShutdownProtocol;
+import com.acgist.taoyao.signal.protocol.client.ClientWakeupProtocol;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
@@ -28,14 +28,22 @@ import io.swagger.v3.oas.annotations.tags.Tag;
@RequestMapping("/client")
public class ClientController {
- @Autowired
- private ClientManager clientManager;
- @Autowired
- private ClientRebootProtocol clientRebootProtocol;
- @Autowired
- private ClientShutdownProtocol clientShutdownProtocol;
+ private final ClientManager clientManager;
+ private final ClientWakeupProtocol clientWakeupProtocol;
+ private final ClientRebootProtocol clientRebootProtocol;
+ private final ClientShutdownProtocol clientShutdownProtocol;
- @Operation(summary = "终端列表", description = "终端列表")
+ public ClientController(
+ ClientManager clientManager, ClientWakeupProtocol clientWakeupProtocol,
+ ClientRebootProtocol clientRebootProtocol, ClientShutdownProtocol clientShutdownProtocol
+ ) {
+ this.clientManager = clientManager;
+ this.clientWakeupProtocol = clientWakeupProtocol;
+ this.clientRebootProtocol = clientRebootProtocol;
+ this.clientShutdownProtocol = clientShutdownProtocol;
+ }
+
+ @Operation(summary = "终端列表", description = "终端列表")
@GetMapping("/list")
@ApiResponse(content = @Content(schema = @Schema(implementation = ClientStatus.class)))
public Message list() {
@@ -49,6 +57,13 @@ public class ClientController {
return Message.success(this.clientManager.status(clientId));
}
+ @Operation(summary = "唤醒终端", description = "唤醒终端")
+ @GetMapping("/wakeup/{clientId}")
+ public Message wakeup(@PathVariable String clientId) {
+ this.clientWakeupProtocol.execute(clientId);
+ return Message.success();
+ }
+
@Operation(summary = "重启终端", description = "重启终端")
@GetMapping("/reboot/{clientId}")
public Message reboot(@PathVariable String clientId) {
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ProtocolController.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ProtocolController.java
index 56648cb..97fa742 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ProtocolController.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/ProtocolController.java
@@ -3,7 +3,6 @@ package com.acgist.taoyao.signal.controller;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -28,9 +27,12 @@ import lombok.extern.slf4j.Slf4j;
@RequestMapping("/protocol")
public class ProtocolController {
- @Autowired
- private ApplicationContext applicationContext;
+ private final ApplicationContext applicationContext;
+ public ProtocolController(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
@Operation(summary = "信令列表", description = "信令列表Markdown")
@GetMapping("/list")
public String list() {
@@ -52,18 +54,11 @@ public class ProtocolController {
}
```
- ### 数据流向
+ ### 符号解释
```
- 请求:终端->信令服务 || 信令服务->媒体服务
- 响应:信令服务->终端 || 服务媒体->信令服务
- 广播:信令服务-)终端 || 信令服务+)终端
- ```
-
- ### 流向解释
-
- ```
- -[消息类型]> 请求(单播):定向请求(单播)信令
+ -[消息类型]> 异步请求 | 定向单播
+ =[消息类型]> 同步请求
-[消息类型]) 全员广播:对所有的终端广播信令(排除自己)
+[消息类型]) 全员广播:对所有的终端广播信令(包含自己)
```
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/RoomController.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/RoomController.java
index 6733038..b961a83 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/RoomController.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/controller/RoomController.java
@@ -2,7 +2,6 @@ package com.acgist.taoyao.signal.controller;
import java.util.List;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -10,9 +9,9 @@ import org.springframework.web.bind.annotation.RestController;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.ClientStatus;
-import com.acgist.taoyao.signal.media.Room;
-import com.acgist.taoyao.signal.media.RoomManager;
-import com.acgist.taoyao.signal.media.RoomStatus;
+import com.acgist.taoyao.signal.terminal.media.Room;
+import com.acgist.taoyao.signal.terminal.media.RoomManager;
+import com.acgist.taoyao.signal.terminal.media.RoomStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
@@ -30,10 +29,13 @@ import io.swagger.v3.oas.annotations.tags.Tag;
@RequestMapping("/room")
public class RoomController {
- @Autowired
- private RoomManager roomManager;
+ private final RoomManager roomManager;
- @Operation(summary = "房间列表", description = "房间列表")
+ public RoomController(RoomManager roomManager) {
+ this.roomManager = roomManager;
+ }
+
+ @Operation(summary = "房间列表", description = "房间列表")
@GetMapping("/list")
@ApiResponse(content = @Content(schema = @Schema(implementation = RoomStatus.class)))
public Message list() {
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ApplicationEventAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ApplicationEventAdapter.java
index e25ea72..1f62064 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ApplicationEventAdapter.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ApplicationEventAdapter.java
@@ -5,7 +5,7 @@ import java.util.Map;
import org.springframework.context.ApplicationEvent;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.MapBodyGetter;
+import com.acgist.taoyao.boot.utils.MapUtils;
import lombok.Getter;
@@ -15,58 +15,58 @@ import lombok.Getter;
* @author acgist
*/
@Getter
-public class ApplicationEventAdapter extends ApplicationEvent implements MapBodyGetter {
+public class ApplicationEventAdapter extends ApplicationEvent {
private static final long serialVersionUID = 1L;
- /**
- * 主体
- */
- private final Map, ?> body;
/**
* 消息
*/
private final Message message;
+ /**
+ * 主体
+ */
+ private final Map body;
- protected ApplicationEventAdapter(Map, ?> body, Message message, Object source) {
+ protected ApplicationEventAdapter(Object source, Message message, Map body) {
super(source);
this.body = body;
this.message = message;
}
/**
- * @see #get(Map, String)
+ * @see MapUtils#get(Map, String)
*/
public T get(String key) {
- return this.get(this.body, key);
+ return MapUtils.get(this.body, key);
}
/**
- * @see #get(Map, String, Object)
+ * @see MapUtils#get(Map, String, Object)
*/
public T get(String key, T defaultValue) {
- return this.get(this.body, key, defaultValue);
+ return MapUtils.get(this.body, key, defaultValue);
}
/**
- * @see #getLong(Map, String)
+ * @see MapUtils#getLong(Map, String)
*/
public Long getLong(String key) {
- return this.getLong(this.body, key);
+ return MapUtils.getLong(this.body, key);
}
/**
- * @see #getInteger(Map, String)
+ * @see MapUtils#getInteger(Map, String)
*/
public Integer getInteger(String key) {
- return this.getInteger(this.body, key);
+ return MapUtils.getInteger(this.body, key);
}
/**
- * @see #getBoolean(Map, String)
+ * @see MapUtils#getBoolean(Map, String)
*/
public Boolean getBoolean(String key) {
- return this.getBoolean(this.body, key);
+ return MapUtils.getBoolean(this.body, key);
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/client/ClientCloseEvent.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ClientCloseEvent.java
similarity index 67%
rename from taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/client/ClientCloseEvent.java
rename to taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ClientCloseEvent.java
index 810898f..e50cb6a 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/client/ClientCloseEvent.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ClientCloseEvent.java
@@ -1,7 +1,6 @@
-package com.acgist.taoyao.signal.event.client;
+package com.acgist.taoyao.signal.event;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.event.ClientEventAdapter;
/**
* 终端关闭事件
@@ -13,7 +12,7 @@ public class ClientCloseEvent extends ClientEventAdapter {
private static final long serialVersionUID = 1L;
public ClientCloseEvent(Client client) {
- super(null, client);
+ super(client);
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ClientEventAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ClientEventAdapter.java
index ea343c9..369fe74 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ClientEventAdapter.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/ClientEventAdapter.java
@@ -26,12 +26,16 @@ public abstract class ClientEventAdapter extends ApplicationEventAdapter {
*/
private final String clientId;
- public ClientEventAdapter(Message message, Client client) {
- this(Map.of(), message, client);
+ public ClientEventAdapter(Client client) {
+ this(client, null, null);
}
- public ClientEventAdapter(Map, ?> body, Message message, Client client) {
- super(body, message, client);
+ public ClientEventAdapter(Client client, Message message) {
+ this(client, message, null);
+ }
+
+ public ClientEventAdapter(Client client, Message message, Map body) {
+ super(client, message, body);
this.client = client;
this.clientId = client.clientId();
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaClientRegisterEvent.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaClientRegisterEvent.java
new file mode 100644
index 0000000..e85bdef
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaClientRegisterEvent.java
@@ -0,0 +1,18 @@
+package com.acgist.taoyao.signal.event;
+
+import com.acgist.taoyao.signal.client.Client;
+
+/**
+ * 媒体服务终端注册事件
+ *
+ * @author acgist
+ */
+public class MediaClientRegisterEvent extends ClientEventAdapter {
+
+ private static final long serialVersionUID = 1L;
+
+ public MediaClientRegisterEvent(Client client) {
+ super(client);
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaEventAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaEventAdapter.java
deleted file mode 100644
index 02e61d2..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaEventAdapter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.acgist.taoyao.signal.event;
-
-import java.util.Map;
-
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.media.MediaClient;
-
-import lombok.Getter;
-
-/**
- * 媒体事件适配器
- *
- * @author acgist
- */
-@Getter
-public class MediaEventAdapter extends ApplicationEventAdapter {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * 媒体服务终端
- */
- private final MediaClient mediaClient;
-
- public MediaEventAdapter(Message message, MediaClient mediaClient) {
- this(Map.of(), message, mediaClient);
- }
-
- public MediaEventAdapter(Map, ?> body, Message message, MediaClient mediaClient) {
- super(body, message, mediaClient);
- this.mediaClient = mediaClient;
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaProduceEvent.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaProduceEvent.java
new file mode 100644
index 0000000..8ed19e1
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/MediaProduceEvent.java
@@ -0,0 +1,30 @@
+package com.acgist.taoyao.signal.event;
+
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.terminal.media.Producer;
+import com.acgist.taoyao.signal.terminal.media.Room;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 媒体生产事件
+ *
+ * @author acgist
+ */
+@Getter
+@Setter
+public class MediaProduceEvent extends RoomEventAdapter {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Client client;
+ private final Producer producer;
+
+ public MediaProduceEvent(Room room, Client client, Producer producer) {
+ super(room);
+ this.client = client;
+ this.producer = producer;
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/RoomEventAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/RoomEventAdapter.java
index 0417d1b..0386c7f 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/RoomEventAdapter.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/RoomEventAdapter.java
@@ -3,7 +3,7 @@ package com.acgist.taoyao.signal.event;
import java.util.Map;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.media.Room;
+import com.acgist.taoyao.signal.terminal.media.Room;
import lombok.Getter;
@@ -22,12 +22,16 @@ public class RoomEventAdapter extends ApplicationEventAdapter {
*/
private final Room room;
- public RoomEventAdapter(Message message, Room room) {
- this(Map.of(), message, room);
+ public RoomEventAdapter(Room room) {
+ this(room, null, null);
}
- public RoomEventAdapter(Map, ?> body, Message message, Room room) {
- super(body, message, room);
+ public RoomEventAdapter(Room room, Message message) {
+ this(room, message, null);
+ }
+
+ public RoomEventAdapter(Room room, Message message, Map body) {
+ super(room, message, body);
this.room = room;
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClient.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClient.java
deleted file mode 100644
index acfa0b4..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClient.java
+++ /dev/null
@@ -1,309 +0,0 @@
-package com.acgist.taoyao.signal.media;
-
-import java.net.URI;
-import java.net.http.WebSocket;
-import java.net.http.WebSocket.Listener;
-import java.nio.ByteBuffer;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.TaskScheduler;
-
-import com.acgist.taoyao.boot.annotation.Prototype;
-import com.acgist.taoyao.boot.config.MediaServerProperties;
-import com.acgist.taoyao.boot.config.TaoyaoProperties;
-import com.acgist.taoyao.boot.model.Header;
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.boot.model.MessageCode;
-import com.acgist.taoyao.boot.model.MessageCodeException;
-import com.acgist.taoyao.boot.utils.HTTPUtils;
-import com.acgist.taoyao.boot.utils.JSONUtils;
-import com.acgist.taoyao.signal.protocol.Protocol;
-import com.acgist.taoyao.signal.protocol.ProtocolManager;
-import com.acgist.taoyao.signal.protocol.media.MediaRegisterProtocol;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 媒体服务终端
- *
- * @author acgist
- */
-@Slf4j
-@Prototype
-public class MediaClient {
-
- @Autowired
- private TaskScheduler taskScheduler;
- @Autowired
- private ProtocolManager protocolManager;
- @Autowired
- private TaoyaoProperties taoyaoProperties;
- @Autowired
- private MediaRegisterProtocol mediaRegisterProtocol;
-
- /**
- * 最长重试周期
- */
- private static final long MAX_DURATION = 60L * 1000;
-
- /**
- * 标识
- */
- private String mediaId;
- /**
- * 重试周期
- */
- private long duration;
- /**
- * 服务通道
- */
- private WebSocket webSocket;
- /**
- * 服务配置
- */
- private MediaServerProperties mediaServerProperties;
- /**
- * 同步消息
- */
- private final Map requestMessage = new ConcurrentHashMap<>();
-
- /**
- * 加载终端
- *
- * @param mediaServerProperties 媒体服务配置
- */
- public void init(MediaServerProperties mediaServerProperties) {
- this.mediaId = mediaServerProperties.getMediaId();
- this.duration = this.taoyaoProperties.getTimeout();
- this.mediaServerProperties = mediaServerProperties;
- this.connectServer();
- }
-
- /**
- * @return 标识
- */
- public String mediaId() {
- return this.mediaId;
- }
-
- /**
- * @return 媒体服务配置
- */
- public MediaServerProperties mediaServerProperties() {
- return this.mediaServerProperties;
- }
-
- /**
- * 心跳
- */
- public void heartbeat() {
- final CompletableFuture future = this.webSocket.sendPing(ByteBuffer.allocate(0));
- try {
- log.debug("媒体服务心跳:{}", this.mediaId);
- future.get(this.taoyaoProperties.getTimeout(), TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- log.error("媒体服务心跳异常:{}", this.mediaId, e);
- }
- }
-
- /**
- * 发送消息
- *
- * @param message 消息
- */
- public void send(Message message) {
- while(this.webSocket == null) {
- Thread.yield();
- }
- this.webSocket.sendText(JSONUtils.toJSON(message), true);
- }
-
- /**
- * 请求消息
- *
- * @param request 消息
- *
- * @return 响应
- */
- public Message request(Message request) {
- final Header header = request.getHeader();
- final String id = header.getId();
- this.requestMessage.put(id, request);
- synchronized (request) {
- this.send(request);
- try {
- request.wait(this.taoyaoProperties.getTimeout());
- } catch (InterruptedException e) {
- log.error("媒体服务等待响应异常:{}", request, e);
- }
- }
- final Message response = this.requestMessage.remove(id);
- if(response == null || request.equals(response)) {
- log.warn("媒体服务没有响应:{}", request);
- throw MessageCodeException.of(MessageCode.CODE_2001, "媒体服务没有响应");
- }
- return response;
- }
-
- /**
- * @return 重试周期
- */
- private long retryDuration() {
- return this.duration = Math.min(this.duration + this.taoyaoProperties.getTimeout(), MAX_DURATION);
- }
-
- /**
- * 连接服务通道
- */
- private void connectServer() {
- final URI uri = URI.create(this.mediaServerProperties.getAddress());
- log.debug("开始连接媒体服务:{}", uri);
- try {
- final WebSocket webSocket = HTTPUtils.newClient()
- .newWebSocketBuilder()
- .connectTimeout(Duration.ofMillis(this.taoyaoProperties.getTimeout()))
- .buildAsync(uri, new MessageListener())
- .get();
- log.info("连接媒体服务成功:{}", webSocket);
- // 关闭旧的通道
- if(this.webSocket != null && !(this.webSocket.isInputClosed() && this.webSocket.isOutputClosed())) {
- this.webSocket.abort();
- }
- // 设置新的通道
- this.webSocket = webSocket;
- // 重置重试周期
- this.duration = this.taoyaoProperties.getTimeout();
- // 发送授权消息
- this.send(this.mediaRegisterProtocol.build(this.mediaServerProperties));
- } catch (Exception e) {
- log.error("连接媒体服务异常:{}", uri, e);
- this.taskScheduler.schedule(
- this::connectServer,
- Instant.now().plusMillis(this.retryDuration())
- );
- }
- }
-
- /**
- * 处理信令消息
- *
- * @param content 信令消息
- */
- private void execute(String content) {
- if(StringUtils.isEmpty(content)) {
- log.warn("媒体服务信令消息格式错误:{}", content);
- return;
- }
- final Message message = JSONUtils.toJava(content, Message.class);
- final Header header = message.getHeader();
- if(header == null) {
- log.warn("媒体服务信令消息格式错误(没有头部):{}", content);
- return;
- }
- final String v = header.getV();
- final String id = header.getId();
- final String signal = header.getSignal();
- if(v == null || id == null || signal == null) {
- log.warn("媒体服务信令消息格式错误(缺失头部关键参数):{}", content);
- return;
- }
- final Message request = this.requestMessage.get(id);
- if(request != null) {
- // 同步处理:重新设置响应消息
- this.requestMessage.put(id, message);
- // 唤醒等待线程
- synchronized (request) {
- request.notifyAll();
- }
- // 同步处理不要执行回调
- } else {
- final Protocol protocol = this.protocolManager.protocol(signal);
- if(protocol == null) {
- log.warn("不支持的媒体信令协议:{}", content);
- } else {
- protocol.execute(this, message);
- }
- }
- }
-
- /**
- * 信令消息监听
- *
- * @author acgist
- */
- public class MessageListener implements Listener {
-
- @Override
- public void onOpen(WebSocket webSocket) {
- log.info("媒体服务通道打开:{}", webSocket);
- Listener.super.onOpen(webSocket);
- }
-
- @Override
- public CompletionStage> onBinary(WebSocket webSocket, ByteBuffer buffer, boolean last) {
- log.debug("媒体服务收到信令消息(binary):{}", webSocket);
- return Listener.super.onBinary(webSocket, buffer, last);
- }
-
- @Override
- public CompletionStage> onText(WebSocket webSocket, CharSequence content, boolean last) {
- log.debug("媒体服务收到信令消息(text):{}-{}", webSocket, content);
- try {
- MediaClient.this.execute(content.toString());
- } catch (Exception e) {
- log.error("处理媒体服务信令消息异常:{}", content, e);
- }
- return Listener.super.onText(webSocket, content, last);
- }
-
- @Override
- public CompletionStage> onClose(WebSocket webSocket, int statusCode, String reason) {
- log.warn("媒体服务通道关闭:{}-{}-{}", webSocket, statusCode, reason);
- try {
- return Listener.super.onClose(webSocket, statusCode, reason);
- } finally {
- MediaClient.this.taskScheduler.schedule(
- MediaClient.this::connectServer,
- Instant.now().plusMillis(MediaClient.this.retryDuration())
- );
- }
- }
-
- @Override
- public void onError(WebSocket webSocket, Throwable error) {
- log.error("媒体服务通道异常:{}", webSocket, error);
- try {
- Listener.super.onError(webSocket, error);
- } finally {
- MediaClient.this.taskScheduler.schedule(
- MediaClient.this::connectServer,
- Instant.now().plusMillis(MediaClient.this.retryDuration())
- );
- }
- }
-
- @Override
- public CompletionStage> onPing(WebSocket webSocket, ByteBuffer buffer) {
- log.debug("媒体服务收到信令消息(ping):{}", webSocket);
- return Listener.super.onPing(webSocket, buffer);
- }
-
- @Override
- public CompletionStage> onPong(WebSocket webSocket, ByteBuffer buffer) {
- log.debug("媒体服务收到信令消息(pong):{}", webSocket);
- return Listener.super.onPong(webSocket, buffer);
- }
-
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClientManager.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClientManager.java
deleted file mode 100644
index e1054a9..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/MediaClientManager.java
+++ /dev/null
@@ -1,71 +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 org.springframework.context.ApplicationContext;
-import org.springframework.scheduling.annotation.Scheduled;
-
-import com.acgist.taoyao.boot.annotation.Manager;
-import com.acgist.taoyao.boot.config.MediaProperties;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 媒体服务终端管理
- *
- * @author acgist
- */
-@Slf4j
-@Manager
-public class MediaClientManager {
-
- @Autowired
- private MediaProperties mediaProperties;
- @Autowired
- private ApplicationContext applicationContext;
-
- /**
- * 媒体服务终端列表
- */
- private Map clientMap = new ConcurrentHashMap<>();
-
- @Scheduled(cron = "${taoyao.scheduled.media:0 * * * * ?}")
- public void scheduled() {
- this.heartbeat();
- }
-
- /**
- * 加载媒体服务终端
- */
- public void init() {
- this.mediaProperties.getMediaServerList().stream()
- .filter(v -> Boolean.TRUE.equals(v.getEnabled()))
- .forEach(v -> {
- final MediaClient client = this.applicationContext.getBean(MediaClient.class);
- client.init(v);
- this.clientMap.put(client.mediaId(), client);
- log.info("注册媒体服务终端:{}-{}", v.getMediaId(), v.getAddress());
- });
- }
-
- /**
- * @param name 媒体服务终端名称
- *
- * @return 媒体服务终端
- */
- public MediaClient mediaClient(String name) {
- return this.clientMap.get(name);
- }
-
- /**
- * 心跳
- */
- private void heartbeat() {
- this.clientMap.forEach((k, v) -> {
- v.heartbeat();
- });
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Peer.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Peer.java
deleted file mode 100644
index a98564d..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Peer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.acgist.taoyao.signal.media;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import com.acgist.taoyao.signal.client.Client;
-
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * Peer
- *
- * @author acgist
- */
-@Getter
-@Setter
-public class Peer {
-
- /**
- * 终端
- */
- private final Client client;
- private String device;
- private String produce;
- private String consume;
- private Object rtpCapabilities;
- private Object sctpCapabilities;
- private Map transports;
- private Map producers;
- private Map consumers;
- private Map dataProducers;
- private Map dataConsumers;
-
- public Peer(Client client) {
- this.client = client;
- this.transports = new ConcurrentHashMap<>();
- this.producers = new ConcurrentHashMap<>();
- this.consumers = new ConcurrentHashMap<>();
- this.dataProducers = new ConcurrentHashMap<>();
- this.dataConsumers = new ConcurrentHashMap<>();
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Room.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Room.java
deleted file mode 100644
index 5868c23..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Room.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package com.acgist.taoyao.signal.media;
-
-import java.io.Closeable;
-import java.util.List;
-import java.util.Objects;
-
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.client.ClientStatus;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 房间
- *
- * @author acgist
- */
-@Slf4j
-@Getter
-@Setter
-public class Room implements Closeable {
-
- /**
- * 房间标识
- */
- private String roomId;
- /**
- * 密码
- * 设置密码之后进入房间需要验证密码
- */
- private String password;
- /**
- * 状态
- */
- private RoomStatus status;
- /**
- * 媒体服务
- */
- private MediaClient mediaClient;
- /**
- * 终端列表
- */
- private List clients;
- /**
- * 传输通道列表
- * TODO:是否需要
- */
- private List transports;
-
- /**
- * @return 终端状态列表
- */
- public List clientStatus() {
- return this.clients.stream()
- .map(Client::status)
- .toList();
- }
-
- /**
- * 终端进入
- *
- * @param client 终端
- */
- public void enter(Client client) {
- synchronized (this.clients) {
- if(this.clients.contains(client)) {
- return;
- }
- if(this.clients.add(client)) {
- this.status.setClientSize(this.status.getClientSize() + 1);
- }
- }
- }
-
- /**
- * 终端离开
- *
- * @param client 终端
- */
- public void leave(Client client) {
- synchronized (this.clients) {
- if(this.clients.remove(client)) {
- this.status.setClientSize(this.status.getClientSize() - 1);
- }
- }
- }
-
- /**
- * 广播消息
- *
- * @param message 消息
- */
- public void broadcast(Message message) {
- this.clients.forEach(v -> v.push(message));
- }
-
- /**
- * 广播消息
- *
- * @param from 发送终端
- * @param message 消息
- */
- public void broadcast(String from, Message message) {
- this.clients.stream()
- .filter(v -> !Objects.equals(from, v.clientId()))
- .forEach(v -> v.push(message));
- }
-
- /**
- * 广播消息
- *
- * @param from 发送终端
- * @param message 消息
- */
- public void broadcast(Client from, Message message) {
- this.clients.stream()
- .filter(v -> v != from)
- .forEach(v -> v.push(message));
- }
-
- /**
- * @see MediaClient#send(Message)
- */
- public void send(Message message) {
- this.mediaClient.send(message);
- }
-
- /**
- * @see MediaClient#request(Message)
- */
- public Message request(Message message) {
- return this.mediaClient.request(message);
- }
-
- @Override
- public void close() {
- log.info("关闭房间:{}", this.roomId);
- this.mediaClient.send(null);
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Transport.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Transport.java
deleted file mode 100644
index 6291e23..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/media/Transport.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.acgist.taoyao.signal.media;
-
-import java.io.Closeable;
-import java.util.Map;
-
-import com.acgist.taoyao.boot.config.Constant;
-import com.acgist.taoyao.signal.MapBodyGetter;
-import com.acgist.taoyao.signal.client.Client;
-
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * 传输通道
- *
- * @author acgist
- */
-@Getter
-@Setter
-public class Transport implements Closeable, MapBodyGetter {
-
- /**
- * 终端
- */
- private final Client client;
- private String transportId;
- private Object iceCandidates;
- private Object iceParameters;
- private Object dtlsParameters;
- private Object sctpParameters;
-
- public Transport(Client client) {
- this.client = client;
- }
-
- /**
- * 拷贝属性
- *
- * @param body 消息主体
- */
- public void copy(Map, ?> body) {
- this.transportId = this.get(body, Constant.TRANSPORT_ID);
- this.iceCandidates = this.get(body, Constant.ICE_CANDIDATES);
- this.iceParameters = this.get(body, Constant.ICE_PARAMETERS);
- this.dtlsParameters = this.get(body, Constant.DTLS_PARAMETERS);
- this.sctpParameters = this.get(body, Constant.SCTP_PARAMETERS);
- }
-
- @Override
- public void close() {
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java
index 85f3eee..c9d8fe8 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java
@@ -4,7 +4,6 @@ import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.boot.model.MessageCode;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.event.ApplicationEventAdapter;
-import com.acgist.taoyao.signal.media.MediaClient;
/**
* 信令
@@ -13,6 +12,7 @@ import com.acgist.taoyao.signal.media.MediaClient;
* media:: 媒体信令
* client:: 终端信令
* system:: 系统信令
+ * control:: 控制信令
* platform:: 平台信令
*
* @author acgist
@@ -40,14 +40,6 @@ public interface Protocol {
return true;
}
- /**
- * 处理媒体服务信令
- *
- * @param mediaClient 媒体服务终端
- * @param message 信令消息
- */
- void execute(MediaClient mediaClient, Message message);
-
/**
* 处理终端信令
*
@@ -110,6 +102,6 @@ public interface Protocol {
*
* @return 信令消息
*/
- Message build(String id, MessageCode code, String message, Object body);
+ Message build(Long id, MessageCode code, String message, Object body);
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java
index e55afd9..238fac9 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java
@@ -1,6 +1,5 @@
package com.acgist.taoyao.signal.protocol;
-import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@@ -9,18 +8,19 @@ import com.acgist.taoyao.boot.model.Header;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.boot.model.MessageCode;
import com.acgist.taoyao.boot.service.IdService;
-import com.acgist.taoyao.signal.MapBodyGetter;
import com.acgist.taoyao.signal.client.ClientManager;
import com.acgist.taoyao.signal.event.ApplicationEventAdapter;
-import com.acgist.taoyao.signal.media.MediaClientManager;
-import com.acgist.taoyao.signal.media.RoomManager;
+import com.acgist.taoyao.signal.terminal.media.RoomManager;
+
+import lombok.extern.slf4j.Slf4j;
/**
* 信令适配器
*
* @author acgist
*/
-public abstract class ProtocolAdapter implements Protocol, MapBodyGetter {
+@Slf4j
+public abstract class ProtocolAdapter implements Protocol {
@Autowired
protected IdService idService;
@@ -32,8 +32,6 @@ public abstract class ProtocolAdapter implements Protocol, MapBodyGetter {
protected TaoyaoProperties taoyaoProperties;
@Autowired
protected ApplicationContext applicationContext;
- @Autowired
- protected MediaClientManager mediaClientManager;
/**
* 信令名称
@@ -90,10 +88,10 @@ public abstract class ProtocolAdapter implements Protocol, MapBodyGetter {
}
@Override
- public Message build(String id, MessageCode code, String message, Object body) {
+ public Message build(Long id, MessageCode code, String message, Object body) {
// 消息标识
- if(StringUtils.isEmpty(id)) {
- id = this.idService.buildIdToString();
+ if(id == null) {
+ id = this.idService.buildId();
}
// 消息头部
final Header header = Header.builder()
@@ -111,4 +109,17 @@ public abstract class ProtocolAdapter implements Protocol, MapBodyGetter {
return build;
}
+ /**
+ * @param args 参数
+ */
+ protected void logNoAdapter(Object ... args) {
+ final StringBuilder builder = new StringBuilder(this.name);
+ builder.append("没有适配信令消息:");
+ for (final Object object : args) {
+ builder.append(object).append(" ");
+ }
+ builder.setLength(builder.length() - 1);
+ log.debug(builder.toString());
+ }
+
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolClientAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolClientAdapter.java
index 5549daa..1ce223e 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolClientAdapter.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolClientAdapter.java
@@ -3,9 +3,8 @@ package com.acgist.taoyao.signal.protocol;
import java.util.Map;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.boot.model.MessageCodeException;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.media.MediaClient;
+import com.acgist.taoyao.signal.client.ClientType;
/**
* 终端信令适配器
@@ -17,32 +16,22 @@ public abstract class ProtocolClientAdapter extends ProtocolAdapter {
protected ProtocolClientAdapter(String name, String signal) {
super(name, signal);
}
-
- @Override
- public void execute(MediaClient mediaClient, Message message) {
- }
@Override
public void execute(Client client, Message message) {
- final Object body = message.getBody();
- if(body instanceof Map, ?> map) {
- this.execute(client.clientId(), map, client, message);
- } else if(body == null) {
- this.execute(client.clientId(), Map.of(), client, message);
- } else {
- throw MessageCodeException.of("信令主体类型错误:" + message);
- }
+ this.execute(client.clientId(), client.clientType(), client, message, message.mapBody());
}
/**
* 处理终端信令
*
* @param clientId 终端标识
- * @param body 消息主体
+ * @param clientType 终端类型
* @param client 终端
- * @param message 信令消息
+ * @param message 消息
+ * @param body 消息主体
*/
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolControlAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolControlAdapter.java
new file mode 100644
index 0000000..9795367
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolControlAdapter.java
@@ -0,0 +1,47 @@
+package com.acgist.taoyao.signal.protocol;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.config.Constant;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.model.MessageCodeException;
+import com.acgist.taoyao.boot.utils.MapUtils;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+
+/**
+ * 控制信令适配器
+ *
+ * @author acgist
+ */
+public class ProtocolControlAdapter extends ProtocolClientAdapter {
+
+ protected ProtocolControlAdapter(String name, String signal) {
+ super(name, signal);
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
+ final String to = MapUtils.get(body, Constant.TO);
+ final Client targetClient = this.clientManager.clients(to);
+ if(targetClient == null) {
+ throw MessageCodeException.of("目标终端无效:" + to);
+ }
+ this.execute(clientId, clientType, client, targetClient, message, body);
+ }
+
+ /**
+ * 处理终端控制信令
+ *
+ * @param clientId 终端标识
+ * @param clientType 终端类型
+ * @param room 房间
+ * @param client 终端
+ * @param targetClient 目标
+ * @param message 消息
+ * @param body 消息主体
+ */
+ public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map body) {
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java
index 180b321..2c76690 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java
@@ -3,7 +3,6 @@ package com.acgist.taoyao.signal.protocol;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import com.acgist.taoyao.boot.annotation.Manager;
@@ -28,20 +27,24 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@Manager
public class ProtocolManager {
+
+ private final ClientManager clientManager;
+ private final SecurityService securityService;
+ private final ApplicationContext applicationContext;
+ private final PlatformErrorProtocol platformErrorProtocol;
+
+ /**
+ * 信令映射
+ */
+ private final Map protocolMapping;
- /**
- * 信令映射
- */
- private Map protocolMapping = new ConcurrentHashMap<>();
-
- @Autowired
- private ClientManager clientManager;
- @Autowired
- private SecurityService securityService;
- @Autowired
- private ApplicationContext applicationContext;
- @Autowired
- private PlatformErrorProtocol platformErrorProtocol;
+ public ProtocolManager(ClientManager clientManager, SecurityService securityService, ApplicationContext applicationContext, PlatformErrorProtocol platformErrorProtocol) {
+ this.clientManager = clientManager;
+ this.securityService = securityService;
+ this.applicationContext = applicationContext;
+ this.platformErrorProtocol = platformErrorProtocol;
+ this.protocolMapping = new ConcurrentHashMap<>();
+ }
/**
* 加载信令映射
@@ -79,7 +82,7 @@ public class ProtocolManager {
*/
public void execute(String content, AutoCloseable instance) {
log.debug("执行信令消息:{}", content);
- final Client client = this.clientManager.client(instance);
+ final Client client = this.clientManager.clients(instance);
if(client == null) {
log.warn("信令终端无效:{}-{}", instance, content);
return;
@@ -98,7 +101,7 @@ public class ProtocolManager {
return;
}
final String v = header.getV();
- final String id = header.getId();
+ final Long id = header.getId();
final String signal = header.getSignal();
// 设置缓存ID
this.platformErrorProtocol.set(id);
@@ -117,7 +120,11 @@ public class ProtocolManager {
if(protocol instanceof ClientRegisterProtocol) {
protocol.execute(client, message);
} else if(this.securityService.authenticate(client, message, protocol)) {
- protocol.execute(client, message);
+ if(client.response(id, message)) {
+ // 响应消息不做其他处理
+ } else {
+ protocol.execute(client, message);
+ }
} else {
log.warn("终端没有授权:{}", content);
client.push(this.platformErrorProtocol.build(MessageCode.CODE_3401, "终端没有授权"));
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolMediaAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolMediaAdapter.java
deleted file mode 100644
index a2e4765..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolMediaAdapter.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.acgist.taoyao.signal.protocol;
-
-import java.util.Map;
-
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.boot.model.MessageCodeException;
-import com.acgist.taoyao.signal.media.MediaClient;
-
-/**
- * 媒体服务信令适配器
- *
- * @author acgist
- */
-public abstract class ProtocolMediaAdapter extends ProtocolClientAdapter {
-
- protected ProtocolMediaAdapter(String name, String signal) {
- super(name, signal);
- }
-
- /**
- * 处理媒体服务信令
- *
- * @param mediaClient 媒体服务终端
- * @param message 信令消息
- */
- public void execute(MediaClient mediaClient, Message message) {
- final Object body = message.getBody();
- if(body instanceof Map, ?> map) {
- this.execute(map, mediaClient, message);
- } else if(body == null) {
- this.execute(Map.of(), mediaClient, message);
- } else {
- throw MessageCodeException.of("信令主体类型错误:" + message);
- }
- }
-
- /**
- * 处理媒体服务信令
- *
- * @param body 消息主体
- * @param mediaClient 媒体服务终端
- * @param message 信令消息
- */
- public void execute(Map, ?> body, MediaClient mediaClient, Message message) {
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolRoomAdapter.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolRoomAdapter.java
index dc3514d..6698a90 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolRoomAdapter.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolRoomAdapter.java
@@ -5,68 +5,45 @@ import java.util.Map;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.boot.model.MessageCodeException;
+import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.media.Room;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.terminal.media.Room;
/**
* 房间信令适配器
*
- * TODO:校验是否是房间内的用户权限
- *
* @author acgist
*/
-public abstract class ProtocolRoomAdapter extends ProtocolMediaAdapter {
+public abstract class ProtocolRoomAdapter extends ProtocolClientAdapter {
protected ProtocolRoomAdapter(String name, String signal) {
super(name, signal);
}
@Override
- public void execute(Map, ?> body, MediaClient mediaClient, Message message) {
- this.execute(this.room(body), body, mediaClient, message);
- }
-
- @Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- this.execute(clientId, this.room(body), body, client, message);
- }
-
- /**
- * @param body 消息主体
- *
- * @return 房间
- */
- protected Room room(Map, ?> body) {
- final String roomId = this.get(body, Constant.ROOM_ID);
- final Room room = this.roomManager.room(roomId);
- if(room == null) {
- throw MessageCodeException.of("无效房间:" + roomId);
- }
- return room;
- }
-
- /**
- * 处理媒体服务房间信令
- *
- * @param room 房间
- * @param body 消息主体
- * @param mediaClient 媒体服务终端
- * @param message 信令消息
- */
- public void execute(Room room, Map, ?> body, MediaClient mediaClient, Message message) {
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
+ final String roomId = MapUtils.get(body, Constant.ROOM_ID);
+ final Room room = this.roomManager.room(roomId);
+ if(room == null) {
+ throw MessageCodeException.of("无效房间:" + roomId);
+ }
+ // TODO:验证用户是否在房间里面
+ this.execute(clientId, clientType, room, client, room.getMediaClient(), message, body);
}
/**
* 处理终端房间信令
*
* @param clientId 终端标识
+ * @param clientType 终端类型
* @param room 房间
- * @param body 消息主体
* @param client 终端
- * @param message 信令消息
+ * @param mediaClient 媒体服务终端
+ * @param message 消息
+ * @param body 消息主体
*/
- public void execute(String clientId, Room room, Map, ?> body, Client client, Message message) {
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientAlarmProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientAlarmProtocol.java
new file mode 100644
index 0000000..ab526ee
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientAlarmProtocol.java
@@ -0,0 +1,54 @@
+package com.acgist.taoyao.signal.protocol.client;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.config.Constant;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.utils.MapUtils;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 终端告警信令
+ *
+ * @author acgist
+ */
+@Slf4j
+@Protocol
+@Description(
+ body = """
+ {
+ "message": "告警描述",
+ "datetime": "告警时间(yyyyMMddHHmmss)"
+ }
+ """,
+ flow = "终端->信令服务"
+)
+public class ClientAlarmProtocol extends ProtocolClientAdapter {
+
+ public static final String SIGNAL = "client::alarm";
+
+ public ClientAlarmProtocol() {
+ super("终端告警信令", SIGNAL);
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
+ log.warn(
+ """
+ 终端发生告警:{}
+ {}
+ {}
+ """,
+ clientId,
+ MapUtils.get(body, Constant.MESSAGE),
+ MapUtils.get(body, Constant.DATETIME)
+ );
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientBroadcastProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientBroadcastProtocol.java
index 343be5b..1dfee75 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientBroadcastProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientBroadcastProtocol.java
@@ -2,10 +2,15 @@ package com.acgist.taoyao.signal.protocol.client;
import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -14,7 +19,16 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
* @author acgist
*/
@Protocol
-@Description(flow = "终端->信令服务-)终端")
+@Description(
+ body = {
+ """
+ {
+ "clientType": "终端类型(可选)"
+ }
+ """
+ },
+ flow = "终端->信令服务-)终端"
+)
public class ClientBroadcastProtocol extends ProtocolClientAdapter {
public static final String SIGNAL = "client::broadcast";
@@ -24,8 +38,13 @@ public class ClientBroadcastProtocol extends ProtocolClientAdapter {
}
@Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- this.clientManager.broadcast(client, message);
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
+ final String queryClientType = MapUtils.get(body, Constant.CLIENT_TYPE);
+ if(StringUtils.isEmpty(queryClientType)) {
+ this.clientManager.broadcast(client, message);
+ } else {
+ this.clientManager.broadcast(client, message, ClientType.of(queryClientType));
+ }
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientCloseProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientCloseProtocol.java
index e78b46f..098d092 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientCloseProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientCloseProtocol.java
@@ -2,15 +2,16 @@ package com.acgist.taoyao.signal.protocol.client;
import java.util.Map;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
+import org.springframework.scheduling.annotation.Async;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.event.client.ClientCloseEvent;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.event.ClientCloseEvent;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
import lombok.extern.slf4j.Slf4j;
@@ -23,29 +24,33 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@Protocol
@Description(
- flow = { "终端->信令服务->终端", "终端->信令服务-[终端下线])终端" }
+ flow = {
+ "终端->信令服务->终端",
+ "终端->信令服务-[终端下线])终端"
+ }
)
public class ClientCloseProtocol extends ProtocolClientAdapter implements ApplicationListener {
public static final String SIGNAL = "client::close";
- @Autowired
- private ClientOfflineProtocol offlineProtocol;
+ private final ClientOfflineProtocol clientOfflineProtocol;
- public ClientCloseProtocol() {
+ public ClientCloseProtocol(ClientOfflineProtocol clientOfflineProtocol) {
super("终端关闭信令", SIGNAL);
+ this.clientOfflineProtocol = clientOfflineProtocol;
}
+ @Async
@Override
public void onApplicationEvent(ClientCloseEvent event) {
this.close(event.getClient());
}
@Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
// 响应消息
client.push(message.cloneWithoutBody());
- // 不用发布事件:关闭连接后会发布事件
+ // 关闭连接后会发布事件
try {
client.close();
} catch (Exception e) {
@@ -68,7 +73,7 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
// 房间释放
this.roomManager.leave(client);
// 广播下线事件
- final Message message = this.offlineProtocol.build(
+ final Message message = this.clientOfflineProtocol.build(
Map.of(Constant.CLIENT_ID, clientId)
);
this.clientManager.broadcast(clientId, message);
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientConfigProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientConfigProtocol.java
index 0c5db42..e42a169 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientConfigProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientConfigProtocol.java
@@ -1,22 +1,20 @@
package com.acgist.taoyao.signal.protocol.client;
import java.time.LocalDateTime;
+import java.util.HashMap;
import java.util.Map;
-import org.springframework.beans.factory.annotation.Autowired;
-
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.config.MediaProperties;
import com.acgist.taoyao.boot.config.WebrtcProperties;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.boot.service.IpService;
import com.acgist.taoyao.boot.utils.DateUtils;
import com.acgist.taoyao.boot.utils.DateUtils.DateTimeStyle;
import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
-import com.acgist.taoyao.signal.wrapper.WebrtcPropertiesWrapper;
/**
* 终端配置信令
@@ -27,8 +25,8 @@ import com.acgist.taoyao.signal.wrapper.WebrtcPropertiesWrapper;
@Description(
body = """
{
- "media": "媒体配置",
- "webrtc": "WebRTC配置",
+ "media": "媒体配置(可选)",
+ "webrtc": "WebRTC配置(可选)",
"datetime": "日期时间(yyyyMMddHHmmss)"
}
""",
@@ -38,36 +36,38 @@ public class ClientConfigProtocol extends ProtocolClientAdapter {
public static final String SIGNAL = "client::config";
- @Autowired
- private IpService ipService;
- @Autowired
- private MediaProperties mediaProperties;
- @Autowired
- private WebrtcProperties webrtcProperties;
+ private final MediaProperties mediaProperties;
+ private final WebrtcProperties webrtcProperties;
- public ClientConfigProtocol() {
+ public ClientConfigProtocol(MediaProperties mediaProperties, WebrtcProperties webrtcProperties) {
super("终端配置信令", SIGNAL);
+ this.mediaProperties = mediaProperties;
+ this.webrtcProperties = webrtcProperties;
}
@Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- client.push(this.build(client));
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
+ client.push(this.build(clientType));
}
/**
- * @param client 终端
+ * @param clientType 终端类型
*
- * @return 信令消息
+ * @return 消息
*/
- public Message build(Client client) {
- final String clientIp = client.ip();
- final Message message = this.build();
- final WebrtcPropertiesWrapper webrtcPropertiesWrapper = new WebrtcPropertiesWrapper(clientIp, this.ipService, this.webrtcProperties);
- message.setBody(Map.of(
- Constant.MEDIA, this.mediaProperties,
- Constant.WEBRTC, webrtcPropertiesWrapper,
- Constant.DATETIME, DateUtils.format(LocalDateTime.now(), DateTimeStyle.YYYYMMDDHH24MMSS)
- ));
+ public Message build(ClientType clientType) {
+ final Message message = super.build();
+ final Map config = new HashMap<>();
+ // 日期时间
+ config.put(Constant.DATETIME, DateUtils.format(LocalDateTime.now(), DateTimeStyle.YYYYMMDDHH24MMSS));
+ // Web、摄像头:媒体配置
+ if(clientType == ClientType.WEB || clientType == ClientType.CAMERA) {
+ config.put(Constant.MEDIA, this.mediaProperties);
+ config.put(Constant.WEBRTC, this.webrtcProperties);
+ } else {
+ this.logNoAdapter(clientType);
+ }
+ message.setBody(config);
return message;
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java
index 9f5bd29..c5623be 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java
@@ -1,14 +1,13 @@
package com.acgist.taoyao.signal.protocol.client;
-import java.time.LocalDateTime;
import java.util.Map;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
-import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientStatus;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -20,9 +19,17 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
@Description(
body = """
{
+ "latitude": 纬度,
+ "longitude": 经度,
+ "humidity": 湿度,
+ "temperature": 温度,
"signal": 信号强度(0~100),
"battery": 电池电量(0~100),
- "charging": 是否正在充电(true|false)
+ "running": 是否正在运行(true|false),
+ "charging": 是否正在充电(true|false),
+ "recording": 是否正在录像(true|false),
+ "status": {更多状态},
+ "config": {更多配置}
}
""",
flow = "终端->信令服务->终端"
@@ -36,15 +43,12 @@ public class ClientHeartbeatProtocol extends ProtocolClientAdapter {
}
@Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
// 响应心跳
client.push(message.cloneWithoutBody());
// 设置状态
final ClientStatus status = client.status();
- status.setSignal(this.get(body, Constant.SIGNAL));
- status.setBattery(this.get(body, Constant.BATTERY));
- status.setCharging(this.get(body, Constant.CHARGING));
- status.setLastHeartbeat(LocalDateTime.now());
+ status.copy(body);
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientListProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientListProtocol.java
index a34dac2..f2c89b8 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientListProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientListProtocol.java
@@ -2,10 +2,15 @@ package com.acgist.taoyao.signal.protocol.client;
import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -16,15 +21,26 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
@Protocol
@Description(
body = """
+ {
+ "clientType": "终端类型(可选)"
+ }
[
{
- "clientId": "终端标识",
"ip": "终端IP",
+ "name": "终端名称",
+ "clientId": "终端标识",
+ "clientType": "终端类型",
+ "latitude": 纬度,
+ "longitude": 经度,
+ "humidity": 湿度,
+ "temperature": 温度,
"signal": 信号强度(0~100),
"battery": 电池电量(0~100),
+ "running": 是否正在运行(true|false),
"charging": 是否正在充电(true|false),
- "mediaId": "媒体服务标识",
- "lastHeartbeat": "最后心跳时间"
+ "recording": 是否正在录像(true|false),
+ "status": {更多状态},
+ "config": {更多配置}
},
...
]
@@ -40,8 +56,13 @@ public class ClientListProtocol extends ProtocolClientAdapter {
}
@Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- message.setBody(this.clientManager.status());
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
+ final String queryClientType = MapUtils.get(body, Constant.CLIENT_TYPE);
+ if(StringUtils.isEmpty(queryClientType)) {
+ message.setBody(this.clientManager.status());
+ } else {
+ message.setBody(this.clientManager.status(ClientType.of(queryClientType)));
+ }
client.push(message);
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOfflineProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOfflineProtocol.java
index dac05c6..6dd6901 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOfflineProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOfflineProtocol.java
@@ -1,11 +1,7 @@
package com.acgist.taoyao.signal.protocol.client;
-import java.util.Map;
-
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -30,9 +26,4 @@ public class ClientOfflineProtocol extends ProtocolClientAdapter {
super("终端下线信令", SIGNAL);
}
- @Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- // 忽略
- }
-
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOnlineProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOnlineProtocol.java
index 122da92..33b3d82 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOnlineProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientOnlineProtocol.java
@@ -1,11 +1,7 @@
package com.acgist.taoyao.signal.protocol.client;
-import java.util.Map;
-
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -18,12 +14,20 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
body = """
{
"ip": "终端IP",
- "mediaId": "媒体服务标识",
+ "name": "终端名称",
"clientId": "终端标识",
+ "clientType": "终端类型",
+ "latitude": 纬度,
+ "longitude": 经度,
+ "humidity": 湿度,
+ "temperature": 温度,
"signal": 信号强度(0~100),
"battery": 电池电量(0~100),
+ "running": 是否正在运行(true|false),
"charging": 是否正在充电(true|false),
- "lastHeartbeat": "最后心跳时间"
+ "recording": 是否正在录像(true|false),
+ "status": {更多状态},
+ "config": {更多配置}
}
""",
flow = "终端-[终端注册]>信令服务-)终端"
@@ -36,9 +40,4 @@ public class ClientOnlineProtocol extends ProtocolClientAdapter {
super("终端上线信令", SIGNAL);
}
- @Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- // 忽略
- }
-
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientRegisterProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientRegisterProtocol.java
index 1a3ef06..ea5764c 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientRegisterProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientRegisterProtocol.java
@@ -1,19 +1,18 @@
package com.acgist.taoyao.signal.protocol.client;
-import java.time.LocalDateTime;
import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.boot.model.MessageCode;
import com.acgist.taoyao.boot.model.MessageCodeException;
+import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientStatus;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.event.MediaClientRegisterEvent;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
import com.acgist.taoyao.signal.service.SecurityService;
@@ -30,62 +29,96 @@ import lombok.extern.slf4j.Slf4j;
@Description(
body = """
{
- "clientId": "终端标识",
"username": "信令用户",
"password": "信令密码",
- "ip": "终端IP(选填)",
+ "name": "终端名称",
+ "clientId": "终端标识",
+ "clientType": "终端类型",
+ "latitude": 纬度,
+ "longitude": 经度,
+ "humidity": 湿度,
+ "temperature": 温度,
"signal": 信号强度(0~100),
"battery": 电池电量(0~100),
- "charging": 是否正在充电(true|false)
+ "running": 是否正在运行(true|false),
+ "charging": 是否正在充电(true|false),
+ "recording": 是否正在录像(true|false),
+ "status": {更多状态},
+ "config": {更多配置}
}
""",
- flow = { "终端->信令服务->终端", "终端->信令服务-[终端上线])终端" }
+ flow = {
+ "终端->信令服务->终端",
+ "终端->信令服务-[终端上线])终端"
+ }
)
public class ClientRegisterProtocol extends ProtocolClientAdapter {
public static final String SIGNAL = "client::register";
- @Autowired
- private SecurityService securityService;
- @Autowired
- private ClientConfigProtocol configProtocol;
- @Autowired
- private ClientOnlineProtocol onlineProtocol;
+ private final SecurityService securityService;
+ private final ClientConfigProtocol clientConfigProtocol;
+ private final ClientOnlineProtocol clientOnlineProtocol;
- public ClientRegisterProtocol() {
+ public ClientRegisterProtocol(SecurityService securityService, ClientConfigProtocol clientConfigProtocol, ClientOnlineProtocol clientOnlineProtocol) {
super("终端注册信令", SIGNAL);
+ this.securityService = securityService;
+ this.clientConfigProtocol = clientConfigProtocol;
+ this.clientOnlineProtocol = clientOnlineProtocol;
}
- @Override
- public void execute(String nullClientId, Map, ?> body, Client client, Message message) {
- final String clientId = this.get(body, Constant.CLIENT_ID);
- final String username = this.get(body, Constant.USERNAME);
- final String password = this.get(body, Constant.PASSWORD);
+ @Override
+ public void execute(String nullClientId, ClientType nullClientType, Client client, Message message, Map body) {
+ final String clientId = MapUtils.get(body, Constant.CLIENT_ID);
+ final String username = MapUtils.get(body, Constant.USERNAME);
+ final String password = MapUtils.get(body, Constant.PASSWORD);
// 如果需要终端鉴权在此实现
if(this.securityService.authenticate(username, password)) {
+ final Client oldClient = this.clientManager.clients(clientId);
+ if(oldClient != null) {
+ log.debug("终端已经存在(注销旧的终端):{}", clientId);
+ oldClient.clientId();
+ }
log.info("终端注册:{}", clientId);
client.authorize(clientId);
message.setCode(MessageCode.CODE_0000);
} else {
throw MessageCodeException.of(MessageCode.CODE_3401, "注册失败");
}
+ final ClientType clientType = ClientType.of(MapUtils.get(body, Constant.CLIENT_TYPE));
// 推送消息
client.push(message.cloneWithoutBody());
// 下发配置
- client.push(this.configProtocol.build(client));
+ client.push(this.clientConfigProtocol.build(clientType));
// 终端状态
- final ClientStatus status = client.status();
- status.setClientId(clientId);
- status.setIp(StringUtils.defaultString(client.ip(), this.get(body, Constant.IP)));
- status.setSignal(this.get(body, Constant.SIGNAL));
- status.setBattery(this.get(body, Constant.BATTERY));
- status.setCharging(this.get(body, Constant.CHARGING));
- status.setLastHeartbeat(LocalDateTime.now());
+ final ClientStatus status = this.buildStatus(clientId, clientType, client, body);
// 上线事件
this.clientManager.broadcast(
clientId,
- this.onlineProtocol.build(status)
+ this.clientOnlineProtocol.build(status)
);
+ // 媒体服务终端注册
+ if(clientType == ClientType.MEDIA) {
+ this.publishEvent(new MediaClientRegisterEvent(client));
+ }
+ }
+
+ /**
+ * @param clientId 终端标识
+ * @param clientType 终端类型
+ * @param client 终端
+ * @param body 消息主体
+ *
+ * @return 终端状态
+ */
+ private ClientStatus buildStatus(String clientId, ClientType clientType, Client client, Map body) {
+ final ClientStatus status = client.status();
+ status.setIp(client.ip());
+ status.setName(MapUtils.get(body, Constant.NAME));
+ status.setClientId(clientId);
+ status.setClientType(clientType);
+ status.copy(body);
+ return status;
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientStatusProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientStatusProtocol.java
index 6c5f250..337163d 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientStatusProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientStatusProtocol.java
@@ -6,7 +6,9 @@ import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -19,7 +21,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
body = {
"""
{
- "clientId": "终端标识"
+ "clientId": "终端标识(可选)"
}
""",
"""
@@ -45,9 +47,9 @@ public class ClientStatusProtocol extends ProtocolClientAdapter {
}
@Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- // 如果没有指定终端标识默认查询自己
- message.setBody(this.clientManager.status(this.get(body, Constant.CLIENT_ID, clientId)));
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
+ final String queryClientId = MapUtils.get(body, Constant.CLIENT_ID, clientId);
+ message.setBody(this.clientManager.status(queryClientId));
client.push(message);
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientUnicastProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientUnicastProtocol.java
index 0fb4bf7..87aebc0 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientUnicastProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientUnicastProtocol.java
@@ -9,6 +9,7 @@ import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
import lombok.extern.slf4j.Slf4j;
@@ -24,7 +25,7 @@ import lombok.extern.slf4j.Slf4j;
body = """
{
"to": "接收终端标识",
- // 主体信息
+ ...自定义的主体
}
""",
flow = "终端->信令服务->终端"
@@ -38,7 +39,7 @@ public class ClientUnicastProtocol extends ProtocolClientAdapter {
}
@Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
+ public void execute(String clientId, ClientType clientType, Client client, Message message, Map body) {
final String to = (String) body.remove(Constant.TO);
if(StringUtils.isNotEmpty(to)) {
this.clientManager.unicast(to, message);
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientWakeupProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientWakeupProtocol.java
index a70413c..76fa782 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientWakeupProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientWakeupProtocol.java
@@ -1,10 +1,31 @@
package com.acgist.taoyao.signal.protocol.client;
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
+
/**
* 终端唤醒信令
*
* @author acgist
*/
-public class ClientWakeupProtocol {
+@Protocol
+@Description(
+ flow = "信令服务->终端"
+)
+public class ClientWakeupProtocol extends ProtocolClientAdapter {
+
+ private static final String SIGNAL = "client::wakeup";
+
+ public ClientWakeupProtocol() {
+ super("终端唤醒信令", SIGNAL);
+ }
+
+ /**
+ * @param clientId 终端标识
+ */
+ public void execute(String clientId) {
+ this.clientManager.unicast(clientId, this.build());
+ }
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlBellProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlBellProtocol.java
new file mode 100644
index 0000000..00131d5
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlBellProtocol.java
@@ -0,0 +1,44 @@
+package com.acgist.taoyao.signal.protocol.control;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.protocol.ProtocolControlAdapter;
+
+/**
+ * 响铃信令
+ *
+ * @author acgist
+ */
+@Protocol
+@Description(
+ flow = {
+ "信令服务->终端",
+ "终端->信令服务->终端"
+ }
+)
+public class ControlBellProtocol extends ProtocolControlAdapter {
+
+ private static final String SIGNAL = "control::bell";
+
+ public ControlBellProtocol() {
+ super("响铃信令", SIGNAL);
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map body) {
+ targetClient.push(message);
+ }
+
+ /**
+ * @param clientId 终端标识
+ */
+ public void execute(String clientId) {
+ this.clientManager.unicast(clientId, this.build());
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPhotographProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPhotographProtocol.java
new file mode 100644
index 0000000..e6517e8
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPhotographProtocol.java
@@ -0,0 +1,44 @@
+package com.acgist.taoyao.signal.protocol.control;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.protocol.ProtocolControlAdapter;
+
+/**
+ * 拍照信令
+ *
+ * @author acgist
+ */
+@Protocol
+@Description(
+ flow = {
+ "信令服务->终端",
+ "终端->信令服务->终端"
+ }
+)
+public class ControlPhotographProtocol extends ProtocolControlAdapter {
+
+ public static final String SIGNAL = "control::photograph";
+
+ public ControlPhotographProtocol() {
+ super("拍照信令", SIGNAL);
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map body) {
+ targetClient.push(message);
+ }
+
+ /**
+ * @param clientId 终端标识
+ */
+ public void execute(String clientId) {
+ this.clientManager.unicast(clientId, this.build());
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPtzProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPtzProtocol.java
new file mode 100644
index 0000000..57a918d
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlPtzProtocol.java
@@ -0,0 +1,68 @@
+package com.acgist.taoyao.signal.protocol.control;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.protocol.ProtocolControlAdapter;
+
+/**
+ * PTZ信令
+ *
+ * @author acgist
+ */
+@Protocol
+@Description(
+ body = """
+ {
+ "type": "PTZ类型(PAN|TILT|ZOOM)",
+ "value": PTZ参数
+ }
+ """,
+ flow = {
+ "信令服务->终端",
+ "终端->信令服务->终端"
+ }
+)
+public class ControlPtzProtocol extends ProtocolControlAdapter {
+
+ public static final String SIGNAL = "control::ptz";
+
+ public ControlPtzProtocol() {
+ super("PTZ信令", SIGNAL);
+ }
+
+ /**
+ * PTZ类型
+ *
+ * @author acgist
+ */
+ public enum Type {
+
+ // 水平
+ PAN,
+ // 垂直
+ TILT,
+ // 变焦
+ ZOOM;
+
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map body) {
+ targetClient.push(message);
+ }
+
+ /**
+ * @param type PTZ类型
+ * @param value PTZ参数
+ * @param clientId 终端标识
+ */
+ public void execute(Type type, Double value, String clientId) {
+ this.clientManager.unicast(clientId, this.build(Map.of(type, value)));
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlRecordProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlRecordProtocol.java
new file mode 100644
index 0000000..248b75c
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/control/ControlRecordProtocol.java
@@ -0,0 +1,45 @@
+package com.acgist.taoyao.signal.protocol.control;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.protocol.ProtocolControlAdapter;
+
+/**
+ * 录像信令
+ *
+ * @author acgist
+ */
+@Protocol
+@Description(
+ memo = "状态通过心跳回传",
+ flow = {
+ "信令服务->终端",
+ "终端->信令服务->终端"
+ }
+)
+public class ControlRecordProtocol extends ProtocolControlAdapter {
+
+ public static final String SIGNAL = "control::record";
+
+ public ControlRecordProtocol() {
+ super("录像信令", SIGNAL);
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Client client, Client targetClient, Message message, Map body) {
+ targetClient.push(message);
+ }
+
+ /**
+ * @param clientId 终端标识
+ */
+ public void execute(String clientId) {
+ this.clientManager.unicast(clientId, this.build());
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAudioActiveSpeakerProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAudioActiveSpeakerProtocol.java
index c97ea62..0e1e2a2 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAudioActiveSpeakerProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAudioActiveSpeakerProtocol.java
@@ -5,9 +5,9 @@ import java.util.Map;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.media.Room;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
+import com.acgist.taoyao.signal.terminal.media.Room;
/**
* 当前讲话终端信令
@@ -17,20 +17,19 @@ import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
@Protocol
public class MediaAudioActiveSpeakerProtocol extends ProtocolRoomAdapter {
- public static final String SIGNAL = "audio::active::speaker";
+ public static final String SIGNAL = "media::audio::active::speaker";
public MediaAudioActiveSpeakerProtocol() {
super("当前讲话终端信令", SIGNAL);
}
@Override
- public void execute(Room room, Map, ?> body, MediaClient mediaClient, Message message) {
- room.broadcast(message);
- }
-
- @Override
- public void execute(String clientId, Room room, Map, ?> body, Client client, Message message) {
- // 忽略
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
+ if(clientType == ClientType.MEDIA) {
+ room.broadcast(message);
+ } else {
+ // 忽略其他情况
+ }
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaConsumeProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaConsumeProtocol.java
new file mode 100644
index 0000000..7186eaa
--- /dev/null
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaConsumeProtocol.java
@@ -0,0 +1,83 @@
+package com.acgist.taoyao.signal.protocol.media;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.context.ApplicationListener;
+import org.springframework.scheduling.annotation.Async;
+
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.config.Constant;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.utils.MapUtils;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.event.MediaProduceEvent;
+import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
+import com.acgist.taoyao.signal.terminal.media.ClientWrapper;
+import com.acgist.taoyao.signal.terminal.media.Producer;
+import com.acgist.taoyao.signal.terminal.media.Room;
+import com.acgist.taoyao.signal.terminal.media.Transport;
+
+/**
+ * 消费媒体信令
+ *
+ * @author acgist
+ */
+@Protocol
+@Description(
+ flow = {
+ "信令服务->媒体服务=>信令服务=>终端->信令服务->信令服务",
+ "终端->信令服务->媒体服务=>信令服务=>终端->信令服务->信令服务"
+ }
+)
+public class MediaConsumeProtocol extends ProtocolRoomAdapter implements ApplicationListener {
+
+ public static final String SIGNAL = "media::consume";
+
+ protected MediaConsumeProtocol() {
+ super("消费媒体信令", SIGNAL);
+ }
+
+ @Async
+ @Override
+ public void onApplicationEvent(MediaProduceEvent event) {
+ // TODO:根据类型进行消费
+ final Room room = event.getRoom();
+ final Client client = event.getClient();
+ final Producer producer = event.getProducer();
+ room.getClients().keySet().stream()
+ .filter(v -> v != client)
+ .forEach(v -> this.consume(room, v, producer));
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
+ final String kind = MapUtils.get(body, Constant.KIND);
+ final String producerId = MapUtils.get(body, Constant.PRODUCER_ID);
+ final Producer producer = room.producer(producerId);
+ final String streamId = producer.getStreamId() + "->" + clientId;
+ if(clientType == ClientType.WEB || clientType == ClientType.CAMERA) {
+ // 请求消费
+ } else if(clientType == ClientType.MEDIA) {
+ // 等待消费者准备完成
+
+ } else {
+ }
+ }
+
+ private void consume(Room room, Client client, Producer producer) {
+ final Client mediaClient = room.getMediaClient();
+ final ClientWrapper clientWrapper = room.client(client);
+ final Transport recvTransport = clientWrapper.getRecvTransport();
+ final Map body = new HashMap<>();
+ body.put(Constant.ROOM_ID, room.getRoomId());
+ body.put(Constant.PRODUCER_ID, producer.getProducerId());
+ body.put(Constant.TRANSPORT_ID, recvTransport.getTransportId());
+ body.put(Constant.RTP_CAPABILITIES, clientWrapper.getRtpCapabilities());
+ body.put(Constant.SCTP_CAPABILITIES, clientWrapper.getSctpCapabilities());
+ mediaClient.push(this.build(body));
+ }
+
+}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaIceRestartProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaIceRestartProtocol.java
index 7826d78..d44ad85 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaIceRestartProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaIceRestartProtocol.java
@@ -1,5 +1,34 @@
package com.acgist.taoyao.signal.protocol.media;
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+
+/**
+ * 媒体重启ICE信令
+ *
+ * @author acgist
+ */
+@Protocol
+@Description(
+ body = {
+ """
+ {
+ "roomId": "房间标识",
+ "transportId": "通道标识"
+ }
+ """,
+ """
+ {
+ "roomId": "房间标识",
+ "transportId": "通道标识",
+ "iceParameters": "iceParameters"
+ }
+ """
+ },
+ flow = "终端->信令服务->媒体服务->信令服务->终端"
+)
public class MediaIceRestartProtocol {
+ public static final String SIGNAL = "media::ice::restart";
+
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaListProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaListProtocol.java
deleted file mode 100644
index 5454910..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaListProtocol.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.acgist.taoyao.signal.protocol.media;
-
-import java.util.Map;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.acgist.taoyao.boot.annotation.Description;
-import com.acgist.taoyao.boot.annotation.Protocol;
-import com.acgist.taoyao.boot.config.MediaProperties;
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
-
-/**
- * 媒体服务列表信令
- *
- * @author acgist
- */
-@Protocol
-@Description(
- body = """
- [
- {
- "name": "名称",
- "enabled": "是否启用",
- "host": "主机",
- "port": "端口",
- "schema": "协议",
- "address": "完整地址"
- }
- ]
- """,
- flow = "终端->信令服务->终端"
-)
-public class MediaListProtocol extends ProtocolClientAdapter {
-
- public static final String SIGNAL = "media::list";
-
- @Autowired
- private MediaProperties mediaProperties;
-
- public MediaListProtocol() {
- super("媒体服务列表信令", SIGNAL);
- }
-
- @Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- message.setBody(this.mediaProperties.getMediaServerList());
- client.push(message);
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProduceProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProduceProtocol.java
index 8221870..abf2d71 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProduceProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProduceProtocol.java
@@ -1,5 +1,77 @@
package com.acgist.taoyao.signal.protocol.media;
-public class MediaProduceProtocol {
+import java.util.Map;
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.config.Constant;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.utils.MapUtils;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.event.MediaProduceEvent;
+import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
+import com.acgist.taoyao.signal.terminal.media.ClientWrapper;
+import com.acgist.taoyao.signal.terminal.media.Producer;
+import com.acgist.taoyao.signal.terminal.media.Room;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 生产媒体信令
+ *
+ * @author acgist
+ */
+@Slf4j
+@Protocol
+@Description(
+ body = {
+ """
+ {
+ "kind": "媒体类型",
+ "roomId": "房间标识",
+ "transportId": "通道标识",
+ "rtpParameters": "rtpParameters"
+ }
+ """
+ },
+ flow = "终端->信令服务->媒体服务->信令服务->终端"
+)
+public class MediaProduceProtocol extends ProtocolRoomAdapter {
+
+ public static final String SIGNAL = "media::produce";
+
+ public MediaProduceProtocol() {
+ super("生产媒体信令", SIGNAL);
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
+ // TODO;类型判断?
+ final String kind = MapUtils.get(body, Constant.KIND);
+ final String streamId = kind + "::" + clientId;
+ body.put(Constant.CLIENT_ID, clientId);
+ body.put(Constant.STREAM_ID, streamId);
+ final Message response = room.request(message);
+ final Map responseBody = response.mapBody();
+ final String producerId = MapUtils.get(responseBody, Constant.PRODUCER_ID);
+ final ClientWrapper clientWrapper = room.client(client);
+ final Map producers = clientWrapper.getProducers();
+ final Producer producer = producers.computeIfAbsent(producerId, key -> new Producer(client, kind, streamId, producerId));
+ final Message responseMessage = response.cloneWithoutBody();
+ responseMessage.setBody(Map.of(
+ Constant.KIND, kind,
+ Constant.STREAM_ID, streamId,
+ Constant.PRODUCER_ID, producerId
+ ));
+ // 根据不同类型进行推送:
+ // 自动推送:不用广播
+ // 音频全收:广播视频
+ // 视频全收:广播音频
+ // 全部不收:全部广播
+ room.broadcast(responseMessage);
+ log.info("{}生产媒体:{} - {} - {}", clientId, kind, streamId, producerId);
+ this.publishEvent(new MediaProduceEvent(room, client, producer));
+ }
+
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProducerScoreProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProducerScoreProtocol.java
index 43889a0..3de0ab2 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProducerScoreProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaProducerScoreProtocol.java
@@ -1,5 +1,35 @@
package com.acgist.taoyao.signal.protocol.media;
-public class MediaProducerScoreProtocol {
+import java.util.Map;
+import com.acgist.taoyao.boot.annotation.Description;
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.client.Client;
+import com.acgist.taoyao.signal.client.ClientType;
+import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
+import com.acgist.taoyao.signal.terminal.media.Room;
+
+/**
+ * 媒体生产者评分信令
+ *
+ * @author acgist
+ */
+@Protocol
+@Description(
+ flow = "媒体服务->信令服务->终端"
+)
+public class MediaProducerScoreProtocol extends ProtocolRoomAdapter {
+
+ public static final String SIGNAL = "media::producer::score";
+
+ public MediaProducerScoreProtocol() {
+ super("媒体生产者评分信令", SIGNAL);
+ }
+
+ @Override
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
+ room.broadcast(message);
+ }
+
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRebootProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRebootProtocol.java
deleted file mode 100644
index 64aa485..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRebootProtocol.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.acgist.taoyao.signal.protocol.media;
-
-import java.util.Map;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.acgist.taoyao.boot.annotation.Description;
-import com.acgist.taoyao.boot.config.ScriptProperties;
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.boot.utils.ScriptUtils;
-import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.protocol.ControlProtocol;
-import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 媒体服务重启信令
- *
- * TODO:指定媒体服务名称
- *
- * @author acgist
- */
-@Slf4j
-@Description(flow = "终端->信令服务+)终端")
-public class MediaRebootProtocol extends ProtocolClientAdapter implements ControlProtocol {
-
- public static final String SIGNAL = "media::reboot";
-
- @Autowired
- private ScriptProperties scriptProperties;
-
- public MediaRebootProtocol() {
- super("重启媒体服务信令", SIGNAL);
- }
-
- /**
- * @param mediaId 媒体服务标识
- */
- public void execute(String mediaId) {
- log.info("重启媒体服务");
- this.clientManager.broadcast(this.build());
- ScriptUtils.execute(this.scriptProperties.getMediaReboot());
- }
-
- @Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- log.info("重启媒体服务:{}", clientId);
- this.clientManager.broadcast(message);
- ScriptUtils.execute(this.scriptProperties.getMediaReboot());
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRecordProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRecordProtocol.java
deleted file mode 100644
index 3dda6c9..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRecordProtocol.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.acgist.taoyao.signal.protocol.media;
-
-public class MediaRecordProtocol {
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRegisterProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRegisterProtocol.java
deleted file mode 100644
index bbbf191..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRegisterProtocol.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.acgist.taoyao.signal.protocol.media;
-
-import java.util.Map;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.acgist.taoyao.boot.annotation.Description;
-import com.acgist.taoyao.boot.annotation.Protocol;
-import com.acgist.taoyao.boot.config.Constant;
-import com.acgist.taoyao.boot.config.MediaServerProperties;
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.protocol.ProtocolMediaAdapter;
-import com.acgist.taoyao.signal.protocol.room.RoomCreateProtocol;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 媒体服务注册信令
- *
- * @author acgist
- */
-@Slf4j
-@Protocol
-@Description(
- body = """
- {
- "username": "媒体用户",
- "password": "媒体密码"
- }
- """,
- flow = "信令服务->媒体服务->信令服务"
-)
-public class MediaRegisterProtocol extends ProtocolMediaAdapter {
-
- public static final String SIGNAL = "media::register";
-
- @Autowired
- private RoomCreateProtocol roomCreateProtocol;
-
- public MediaRegisterProtocol() {
- super("媒体服务注册信令", SIGNAL);
- }
-
- /**
- * 创建信令消息
- *
- * @param mediaServerProperties 媒体服务配置
- *
- * @return 信令消息
- */
- public Message build(MediaServerProperties mediaServerProperties) {
- return super.build(Map.of(
- Constant.USERNAME, mediaServerProperties.getUsername(),
- Constant.PASSWORD, mediaServerProperties.getPassword()
- ));
- }
-
- @Override
- public void execute(Map, ?> body, MediaClient mediaClient, Message message) {
- log.info("媒体终端注册结果:{}", message);
- this.roomManager.recreate(mediaClient, this.roomCreateProtocol.build());
- }
-
- @Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRouterRtpCapabilitiesProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRouterRtpCapabilitiesProtocol.java
index f00cf46..6ad668e 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRouterRtpCapabilitiesProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaRouterRtpCapabilitiesProtocol.java
@@ -6,9 +6,9 @@ import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.media.Room;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
+import com.acgist.taoyao.signal.terminal.media.Room;
/**
* 路由RTP能力信令
@@ -41,13 +41,12 @@ public class MediaRouterRtpCapabilitiesProtocol extends ProtocolRoomAdapter {
}
@Override
- public void execute(Room room, Map, ?> body, MediaClient mediaClient, Message message) {
- // 忽略
- }
-
- @Override
- public void execute(String clientId, Room room, Map, ?> body, Client client, Message message) {
- client.push(room.request(message));
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
+ if(clientType == ClientType.WEB || clientType == ClientType.CAMERA) {
+ client.push(room.request(message));
+ } else {
+ // 忽略其他情况
+ }
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaShutdownProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaShutdownProtocol.java
deleted file mode 100644
index 7412b39..0000000
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaShutdownProtocol.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.acgist.taoyao.signal.protocol.media;
-
-import java.util.Map;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.acgist.taoyao.boot.config.ScriptProperties;
-import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.boot.utils.ScriptUtils;
-import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.protocol.ControlProtocol;
-import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 关闭媒体服务信令
- *
- * TODO:指定媒体服务名称
- *
- * @author acgist
- */
-@Slf4j
-public class MediaShutdownProtocol extends ProtocolClientAdapter implements ControlProtocol {
-
- public static final String SIGNAL = "media::shutdown";
-
- @Autowired
- private ScriptProperties scriptProperties;
-
- public MediaShutdownProtocol() {
- super("关闭媒体服务信令", SIGNAL);
- }
-
- /**
- * @param mediaId 媒体服务标识
- */
- public void execute(String mediaId) {
- log.info("关闭媒体服务");
- this.clientManager.broadcast(this.build());
- ScriptUtils.execute(this.scriptProperties.getMediaShutdown());
- }
-
- @Override
- public void execute(String clientId, Map, ?> body, Client client, Message message) {
- log.info("关闭媒体服务:{}", clientId);
- this.clientManager.broadcast(message);
- ScriptUtils.execute(this.scriptProperties.getMediaShutdown());
- }
-
-}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcConnectProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcConnectProtocol.java
index 74a2e57..4a90704 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcConnectProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcConnectProtocol.java
@@ -4,17 +4,22 @@ import java.util.Map;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.media.Room;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
+import com.acgist.taoyao.signal.terminal.media.Room;
+
+import lombok.extern.slf4j.Slf4j;
/**
* 连接WebRTC通道信令
*
* @author acgist
*/
+@Slf4j
@Protocol
@Description(
body = {
@@ -32,13 +37,16 @@ public class MediaTransportWebRtcConnectProtocol extends ProtocolRoomAdapter {
}
@Override
- public void execute(Room room, Map, ?> body, MediaClient mediaClient, Message message) {
- }
-
- @Override
- public void execute(String clientId, Room room, Map, ?> body, Client client, Message message) {
- final Message response = room.request(message);
- client.push(response);
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
+ if(clientType == ClientType.WEB || clientType == ClientType.CAMERA) {
+ final Message response = room.request(message);
+ final Map responseBody = response.mapBody();
+ client.push(response);
+ final String transportId = MapUtils.get(responseBody, Constant.TRANSPORT_ID);
+ log.info("{} 连接WebRTC信令通道:{}", clientId, transportId);
+ } else {
+ // 忽略其他情况
+ }
}
}
diff --git a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcCreateProtocol.java b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcCreateProtocol.java
index 9b76954..e7b8190 100644
--- a/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcCreateProtocol.java
+++ b/taoyao-signal-server/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaTransportWebRtcCreateProtocol.java
@@ -4,21 +4,20 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
-import com.acgist.taoyao.boot.config.MediaServerProperties;
import com.acgist.taoyao.boot.model.Message;
-import com.acgist.taoyao.boot.service.IpService;
+import com.acgist.taoyao.boot.utils.MapUtils;
+import com.acgist.taoyao.boot.utils.NetUtils;
import com.acgist.taoyao.signal.client.Client;
-import com.acgist.taoyao.signal.media.MediaClient;
-import com.acgist.taoyao.signal.media.Peer;
-import com.acgist.taoyao.signal.media.Room;
-import com.acgist.taoyao.signal.media.Transport;
+import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
+import com.acgist.taoyao.signal.terminal.media.ClientWrapper;
+import com.acgist.taoyao.signal.terminal.media.Room;
+import com.acgist.taoyao.signal.terminal.media.Transport;
import lombok.extern.slf4j.Slf4j;
@@ -51,33 +50,44 @@ public class MediaTransportWebRtcCreateProtocol extends ProtocolRoomAdapter {
public static final String SIGNAL = "media::transport::webrtc::create";
- @Autowired
- private IpService ipService;
-
protected MediaTransportWebRtcCreateProtocol() {
super("创建WebRTC通道信令", SIGNAL);
}
@Override
- public void execute(Room room, Map, ?> body, MediaClient mediaClient, Message message) {
- }
-
- @Override
- public void execute(String clientId, Room room, Map, ?> body, Client client, Message message) {
+ public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map body) {
+ body.put(Constant.CLIENT_ID, clientId);
final Message response = room.request(message);
- final Peer peer = client.peer();
- final Map, ?> responseBody = (Map, ?>) response.getBody();
- final MediaServerProperties mediaServerProperties = room.getMediaClient().mediaServerProperties();
- if(Boolean.TRUE.equals(mediaServerProperties.getRewriteIp())) {
- // 重写IP地址
- this.rewriteIp(client.ip(), responseBody, mediaServerProperties);
+ final Map responseBody = response.mapBody();
+ final String transportId = MapUtils.get(responseBody, Constant.TRANSPORT_ID);
+ // 重写地址
+ this.rewriteIp(client.ip(), responseBody);
+ // 处理逻辑
+ final ClientWrapper clientWrapper = room.client(client);
+ // 消费者
+ final Boolean consuming = MapUtils.getBoolean(body, Constant.CONSUMING);
+ if(Boolean.TRUE.equals(consuming)) {
+ Transport recvTransport = clientWrapper.getRecvTransport();
+ if(recvTransport == null) {
+ recvTransport = new Transport(transportId, room, client);
+ clientWrapper.setRecvTransport(recvTransport);
+ }
+ // 拷贝属性
+ recvTransport.copy(responseBody);
+ }
+ // 生产者
+ final Boolean producing = MapUtils.getBoolean(body, Constant.PRODUCING);
+ if(Boolean.TRUE.equals(producing)) {
+ Transport sendTransport = clientWrapper.getSendTransport();
+ if(sendTransport == null) {
+ sendTransport = new Transport(transportId, room, client);
+ clientWrapper.setSendTransport(sendTransport);
+ }
+ // 拷贝属性
+ sendTransport.copy(responseBody);
}
- final Map transports = peer.getTransports();
- final String transportId = this.get(responseBody, Constant.TRANSPORT_ID);
- final Transport transport = transports.computeIfAbsent(transportId, key -> new Transport(client));
- // 拷贝属性
- transport.copy(responseBody);
client.push(response);
+ log.info("{} 创建WebRTC信令通道:{}", clientId, transportId);
}
/**
@@ -85,22 +95,21 @@ public class MediaTransportWebRtcCreateProtocol extends ProtocolRoomAdapter {
*
* @param clientIp 终端IP
* @param body 消息主体
- * @param mediaServerProperties 媒体服务配置
*/
- private void rewriteIp(String clientIp, Map, ?> body, MediaServerProperties mediaServerProperties) {
- final List