[+] 信令调整
This commit is contained in:
@@ -13,3 +13,7 @@
|
||||
|
||||
[信令格式](https://localhost:8888/protocol/list)
|
||||
|
||||
## TODO
|
||||
|
||||
标识 -> ID
|
||||
所有字段获取 -> get
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.3</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -2,19 +2,18 @@ package com.acgist.taoyao.boot.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
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
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Description {
|
||||
@@ -22,7 +21,7 @@ public @interface Description {
|
||||
/**
|
||||
* @return 消息主体
|
||||
*/
|
||||
String[] body() default { "{}" };
|
||||
String[] body() default { "" };
|
||||
|
||||
/**
|
||||
* @return 数据流向
|
||||
|
||||
@@ -51,6 +51,10 @@ public interface Constant {
|
||||
* 电池电量(0~100)
|
||||
*/
|
||||
String BATTERY = "battery";
|
||||
/**
|
||||
* 是否发生告警
|
||||
*/
|
||||
String ALARMING = "alarming";
|
||||
/**
|
||||
* 是否正在充电
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,8 @@ import lombok.Setter;
|
||||
@ConfigurationProperties(prefix = "taoyao.webrtc")
|
||||
public class WebrtcProperties {
|
||||
|
||||
@Schema(title = "是否加密", description = "是否加密")
|
||||
private Boolean encrypt;
|
||||
@Schema(title = "STUN服务器", description = "STUN服务器")
|
||||
private WebrtcStunProperties[] stun;
|
||||
@Schema(title = "TURN服务器", description = "TURN服务器")
|
||||
|
||||
@@ -58,8 +58,8 @@ public class SecurityInterceptor extends InterceptorAdapter {
|
||||
if(this.permit(request) || this.authorization(request)) {
|
||||
return true;
|
||||
}
|
||||
if(log.isInfoEnabled()) {
|
||||
log.info("授权失败:{}", request.getRequestURL());
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("授权失败:{}", request.getRequestURL());
|
||||
}
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic Realm=\"" + this.securityProperties.getRealm() + "\"");
|
||||
|
||||
@@ -17,9 +17,11 @@ server:
|
||||
port-header: X-Forwarded-Port
|
||||
protocol-header: X-Forwarded-Proto
|
||||
remote-ip-header: X-Forwarded-For
|
||||
# 服务前缀
|
||||
# servlet:
|
||||
# context-path: /taoyao
|
||||
spring:
|
||||
# 快速启动
|
||||
# main:
|
||||
# lazy-initialization: true
|
||||
profiles:
|
||||
@@ -54,10 +56,13 @@ taoyao:
|
||||
name: 桃夭信令服务
|
||||
version: 1.0.0
|
||||
description: 桃夭WebRTC信令服务
|
||||
# 全局超时时间
|
||||
timeout: 5000
|
||||
id:
|
||||
# 服务端
|
||||
max-index: 999999
|
||||
server-index: 0
|
||||
# 终端
|
||||
min-client-index: 10000
|
||||
max-client-index: 99999
|
||||
# 媒体配置
|
||||
@@ -117,6 +122,8 @@ taoyao:
|
||||
buffer-size: 2048
|
||||
# WebRTC配置
|
||||
webrtc:
|
||||
# 是否加密
|
||||
encrypt: false
|
||||
# STUN服务
|
||||
stun:
|
||||
- host: 192.168.1.110
|
||||
@@ -146,7 +153,7 @@ taoyao:
|
||||
password: taoyao
|
||||
# 定时任务
|
||||
scheduled:
|
||||
media: 0 * * * * ?
|
||||
# 清理无效终端连接
|
||||
client: 0 * * * * ?
|
||||
# 地址重写
|
||||
ip-rewrite:
|
||||
|
||||
@@ -25,7 +25,7 @@ public class ClientStatus {
|
||||
private String ip;
|
||||
@Schema(title = "终端名称", description = "终端名称")
|
||||
private String name;
|
||||
@Schema(title = "终端标识", description = "终端标识")
|
||||
@Schema(title = "终端ID", description = "终端ID")
|
||||
private String clientId;
|
||||
@Schema(title = "终端类型", description = "终端类型")
|
||||
private ClientType clientType;
|
||||
@@ -41,6 +41,8 @@ public class ClientStatus {
|
||||
private Integer signal;
|
||||
@Schema(title = "电池电量(0~100)", description = "电池电量(0~100)")
|
||||
private Integer battery;
|
||||
@Schema(title = "是否发生告警", description = "是否发生告警")
|
||||
private Boolean alarming;
|
||||
@Schema(title = "是否正在充电", description = "是否正在充电")
|
||||
private Boolean charging;
|
||||
@Schema(title = "是否正在录像", description = "是否正在录像")
|
||||
@@ -64,11 +66,12 @@ public class ClientStatus {
|
||||
this.setTemperature(MapUtils.get(body, Constant.TEMPERATURE));
|
||||
this.setSignal(MapUtils.get(body, Constant.SIGNAL));
|
||||
this.setBattery(MapUtils.get(body, Constant.BATTERY));
|
||||
this.setCharging(MapUtils.get(body, Constant.CHARGING));
|
||||
this.setRecording(MapUtils.get(body, Constant.RECORDING));
|
||||
this.setLastHeartbeat(LocalDateTime.now());
|
||||
this.setAlarming(MapUtils.getBoolean(body, Constant.ALARMING));
|
||||
this.setCharging(MapUtils.getBoolean(body, Constant.CHARGING));
|
||||
this.setRecording(MapUtils.getBoolean(body, Constant.RECORDING));
|
||||
this.status(MapUtils.get(body, Constant.STATUS));
|
||||
this.config(MapUtils.get(body, Constant.CONFIG));
|
||||
this.setLastHeartbeat(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.acgist.taoyao.signal.controller;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -74,27 +76,39 @@ public class ProtocolController {
|
||||
final Protocol protocol = e.getValue();
|
||||
final String name = protocol.name();
|
||||
final String signal = protocol.signal();
|
||||
final Description annotation = protocol.getClass().getDeclaredAnnotation(Description.class);
|
||||
if(annotation == null) {
|
||||
log.info("信令没有注解:{}-{}", key, name);
|
||||
final Class<?> clazz;
|
||||
final Description description;
|
||||
if(AopUtils.isAopProxy(e) || AopUtils.isCglibProxy(protocol) || AopUtils.isJdkDynamicProxy(protocol)) {
|
||||
// 代理获取
|
||||
clazz = AopUtils.getTargetClass(protocol);
|
||||
description = AnnotationUtils.findAnnotation(clazz, Description.class);
|
||||
} else {
|
||||
// 直接获取
|
||||
clazz = protocol.getClass();
|
||||
description = AnnotationUtils.findAnnotation(clazz, Description.class);
|
||||
}
|
||||
if(description == null) {
|
||||
log.info("信令没有注解:{} - {}", key, name);
|
||||
return;
|
||||
}
|
||||
// 信令名称
|
||||
builder.append("### ").append(name).append("(").append(signal).append(")")
|
||||
.append(Constant.LINE).append(Constant.LINE)
|
||||
.append("```").append(Constant.LINE);
|
||||
// 消息主体
|
||||
builder.append("# 消息主体").append(Constant.LINE);
|
||||
Stream.of(annotation.body()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
|
||||
// 数据流向
|
||||
builder.append("# 数据流向").append(Constant.LINE);
|
||||
Stream.of(annotation.flow()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
|
||||
builder.append("```").append(Constant.LINE).append(Constant.LINE);
|
||||
builder.append("### ").append(name)
|
||||
.append("(").append(signal).append(")")
|
||||
.append(Constant.LINE).append(Constant.LINE);
|
||||
// 描述信息
|
||||
final String memo = annotation.memo().strip();
|
||||
final String memo = description.memo().strip();
|
||||
if(StringUtils.isNotEmpty(memo)) {
|
||||
builder.append(memo).append(Constant.LINE).append(Constant.LINE);
|
||||
}
|
||||
// 消息主体
|
||||
builder
|
||||
.append("```").append(Constant.LINE)
|
||||
.append("# 消息主体").append(Constant.LINE);
|
||||
Stream.of(description.body()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
|
||||
// 数据流向
|
||||
builder.append("# 数据流向").append(Constant.LINE);
|
||||
Stream.of(description.flow()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
|
||||
builder.append("```").append(Constant.LINE).append(Constant.LINE);
|
||||
});
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.acgist.taoyao.signal.event;
|
||||
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 终端配置事件
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ClientConfigEvent extends ClientEventAdapter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ClientConfigEvent(Client client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.acgist.taoyao.signal.event;
|
||||
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 终端下线事件
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ClientOfflineEvent extends ClientEventAdapter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ClientOfflineEvent(Client client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.acgist.taoyao.signal.event;
|
||||
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 终端上线事件
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ClientOnlineEvent extends ClientEventAdapter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ClientOnlineEvent(Client client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.acgist.taoyao.signal.event;
|
||||
|
||||
import com.acgist.taoyao.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 离开房间事件
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class RoomLeaveEvent extends RoomEventAdapter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 离开终端
|
||||
*/
|
||||
private final Client client;
|
||||
|
||||
public RoomLeaveEvent(Room room, Client client) {
|
||||
super(room);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import lombok.Setter;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ClientWrapper {
|
||||
public class ClientWrapper implements AutoCloseable {
|
||||
|
||||
/**
|
||||
* 媒体订阅类型
|
||||
@@ -131,5 +131,9 @@ public class ClientWrapper {
|
||||
return this.producers.values().stream()
|
||||
.anyMatch(v -> v.getConsumers().values().stream().anyMatch(c -> c.getProducer() == producer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,14 +87,21 @@ public class Room implements Closeable {
|
||||
|
||||
/**
|
||||
* 终端离开
|
||||
* 立即释放所有资源
|
||||
*
|
||||
* @param client 终端
|
||||
*/
|
||||
public void leave(Client client) {
|
||||
synchronized (this.clients) {
|
||||
if(this.clients.remove(client) != null) {
|
||||
final ClientWrapper wrapper = this.clients.remove(client);
|
||||
if(wrapper != null) {
|
||||
try {
|
||||
wrapper.close();
|
||||
} catch (Exception e) {
|
||||
log.error("终端关闭异常", e);
|
||||
}
|
||||
this.roomStatus.setClientSize(this.roomStatus.getClientSize() - 1);
|
||||
// TODO:资源释放
|
||||
// TODO:leave事件
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,6 @@ public class ProtocolManager {
|
||||
* @param instance 终端实例
|
||||
*/
|
||||
public void execute(String content, AutoCloseable instance) {
|
||||
log.debug("执行信令消息:{}", content);
|
||||
final Client client = this.clientManager.clients(instance);
|
||||
if(client == null) {
|
||||
log.warn("信令终端无效:{}-{}", instance, content);
|
||||
@@ -117,6 +116,9 @@ public class ProtocolManager {
|
||||
client.push(this.platformErrorProtocol.build("不支持的信令协议:" + signal));
|
||||
return;
|
||||
}
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("执行信令消息:{} - {}", client.clientId(), content);
|
||||
}
|
||||
if(protocol instanceof ClientRegisterProtocol) {
|
||||
protocol.execute(client, message);
|
||||
} else if(this.securityService.authenticate(client, message, protocol)) {
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.ClientStatus;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
|
||||
@@ -53,6 +54,8 @@ public class ClientAlarmProtocol extends ProtocolClientAdapter {
|
||||
alarmMessage,
|
||||
alarmDatetime
|
||||
);
|
||||
final ClientStatus status = client.status();
|
||||
status.setAlarming(Boolean.TRUE);
|
||||
// 业务逻辑
|
||||
}
|
||||
|
||||
|
||||
@@ -20,16 +20,16 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
memo = "没有指定终端类型时广播所有类型终端",
|
||||
body = {
|
||||
"""
|
||||
{
|
||||
"clientType": "终端类型(可选)"
|
||||
...
|
||||
}
|
||||
}
|
||||
"""
|
||||
},
|
||||
flow = "终端->信令服务-)终端",
|
||||
memo = "没有指定终端类型时广播所有类型终端"
|
||||
flow = "终端->信令服务-)终端"
|
||||
)
|
||||
public class ClientBroadcastProtocol extends ProtocolClientAdapter {
|
||||
|
||||
|
||||
@@ -7,23 +7,24 @@ 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.client.ClientType;
|
||||
import com.acgist.taoyao.signal.event.ClientCloseEvent;
|
||||
import com.acgist.taoyao.signal.event.ClientOfflineEvent;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 终端关闭信令
|
||||
* 关闭终端信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
@Protocol
|
||||
@Description(
|
||||
memo = "同时释放所有资源,所以如果终端意外掉线重连,需要终端实现音视频重连逻辑。",
|
||||
flow = {
|
||||
"终端->信令服务->终端",
|
||||
"终端->信令服务-[终端下线])终端"
|
||||
@@ -33,11 +34,8 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
|
||||
|
||||
public static final String SIGNAL = "client::close";
|
||||
|
||||
private final ClientOfflineProtocol clientOfflineProtocol;
|
||||
|
||||
public ClientCloseProtocol(ClientOfflineProtocol clientOfflineProtocol) {
|
||||
super("终端关闭信令", SIGNAL);
|
||||
this.clientOfflineProtocol = clientOfflineProtocol;
|
||||
public ClientCloseProtocol() {
|
||||
super("关闭终端信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Async
|
||||
@@ -48,13 +46,12 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
|
||||
// 响应消息
|
||||
client.push(message.cloneWithoutBody());
|
||||
// 关闭连接后会发布事件
|
||||
try {
|
||||
// 关闭连接后会发布事件
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭终端异常", e);
|
||||
log.error("关闭终端异常:{}", clientId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,24 +60,17 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
|
||||
*
|
||||
* @param client 终端
|
||||
*/
|
||||
public void close(Client client) {
|
||||
if(!client.authorized()) {
|
||||
private void close(Client client) {
|
||||
if(client == null || !client.authorized()) {
|
||||
// 没有授权终端
|
||||
return;
|
||||
}
|
||||
final String clientId = client.clientId();
|
||||
log.info("关闭终端:{}", clientId);
|
||||
// 房间释放
|
||||
// 释放房间终端
|
||||
this.roomManager.leave(client);
|
||||
// 广播下线事件
|
||||
final Message message = this.clientOfflineProtocol.build(
|
||||
Map.of(Constant.CLIENT_ID, clientId)
|
||||
);
|
||||
this.clientManager.broadcast(clientId, message);
|
||||
// TODO:释放连接
|
||||
// TODO:释放房间
|
||||
// TODO:退出帐号
|
||||
// TODO:注意释放:是否考虑没有message(非正常的关闭)不要立即释放
|
||||
// 终端下线事件
|
||||
this.publishEvent(new ClientOfflineEvent(client));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import java.time.LocalDateTime;
|
||||
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;
|
||||
@@ -14,6 +17,7 @@ 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.event.ClientConfigEvent;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
|
||||
/**
|
||||
@@ -30,9 +34,9 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
"datetime": "日期时间(yyyyMMddHHmmss)"
|
||||
}
|
||||
""",
|
||||
flow = "终端-[终端注册]>信令服务->终端"
|
||||
flow = "终端=[终端注册]>信令服务->终端"
|
||||
)
|
||||
public class ClientConfigProtocol extends ProtocolClientAdapter {
|
||||
public class ClientConfigProtocol extends ProtocolClientAdapter implements ApplicationListener<ClientConfigEvent> {
|
||||
|
||||
public static final String SIGNAL = "client::config";
|
||||
|
||||
@@ -45,6 +49,14 @@ public class ClientConfigProtocol extends ProtocolClientAdapter {
|
||||
this.webrtcProperties = webrtcProperties;
|
||||
}
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void onApplicationEvent(ClientConfigEvent event) {
|
||||
final Client client = event.getClient();
|
||||
final ClientType clientType = client.clientType();
|
||||
client.push(this.build(clientType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
|
||||
client.push(this.build(clientType));
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
"temperature": 温度,
|
||||
"signal": 信号强度(0~100),
|
||||
"battery": 电池电量(0~100),
|
||||
"alarming": 是否发生告警(true|false),
|
||||
"charging": 是否正在充电(true|false),
|
||||
"recording": 是否正在录像(true|false),
|
||||
"status": {更多状态},
|
||||
@@ -43,9 +44,7 @@ public class ClientHeartbeatProtocol extends ProtocolClientAdapter {
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
|
||||
// 响应心跳
|
||||
client.push(message.cloneWithoutBody());
|
||||
// 设置状态
|
||||
final ClientStatus status = client.status();
|
||||
status.copy(body);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
memo = "没有选择终端类型时返回所有类型终端状态列表",
|
||||
body = """
|
||||
{
|
||||
"clientType": "终端类型(可选)"
|
||||
@@ -36,6 +37,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
"temperature": 温度,
|
||||
"signal": 信号强度(0~100),
|
||||
"battery": 电池电量(0~100),
|
||||
"alarming": 是否发生告警(true|false),
|
||||
"charging": 是否正在充电(true|false),
|
||||
"recording": 是否正在录像(true|false),
|
||||
"status": {更多状态},
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package com.acgist.taoyao.signal.protocol.client;
|
||||
|
||||
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.signal.event.ClientOfflineEvent;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
|
||||
/**
|
||||
@@ -18,7 +25,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
""",
|
||||
flow = "终端-[终端关闭]>信令服务-)终端"
|
||||
)
|
||||
public class ClientOfflineProtocol extends ProtocolClientAdapter {
|
||||
public class ClientOfflineProtocol extends ProtocolClientAdapter implements ApplicationListener<ClientOfflineEvent> {
|
||||
|
||||
public static final String SIGNAL = "client::offline";
|
||||
|
||||
@@ -26,4 +33,13 @@ public class ClientOfflineProtocol extends ProtocolClientAdapter {
|
||||
super("终端下线信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void onApplicationEvent(ClientOfflineEvent event) {
|
||||
final String clientId = event.getClientId();
|
||||
this.clientManager.broadcast(clientId, this.build(
|
||||
Map.of(Constant.CLIENT_ID, clientId)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.acgist.taoyao.signal.protocol.client;
|
||||
|
||||
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.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.event.ClientOnlineEvent;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
|
||||
/**
|
||||
@@ -23,20 +28,32 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
"temperature": 温度,
|
||||
"signal": 信号强度(0~100),
|
||||
"battery": 电池电量(0~100),
|
||||
"alarming": 是否发生告警(true|false),
|
||||
"charging": 是否正在充电(true|false),
|
||||
"recording": 是否正在录像(true|false),
|
||||
"status": {更多状态},
|
||||
"config": {更多配置}
|
||||
}
|
||||
""",
|
||||
flow = "终端-[终端注册]>信令服务-)终端"
|
||||
flow = "终端=[终端注册]>信令服务-)终端"
|
||||
)
|
||||
public class ClientOnlineProtocol extends ProtocolClientAdapter {
|
||||
public class ClientOnlineProtocol extends ProtocolClientAdapter implements ApplicationListener<ClientOnlineEvent> {
|
||||
|
||||
public static final String SIGNAL = "client::online";
|
||||
|
||||
public ClientOnlineProtocol() {
|
||||
super("终端上线信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void onApplicationEvent(ClientOnlineEvent event) {
|
||||
final Client client = event.getClient();
|
||||
final String clientId = event.getClientId();
|
||||
this.clientManager.broadcast(
|
||||
clientId,
|
||||
this.build(client.status())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,10 +8,13 @@ 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.CloseableUtils;
|
||||
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.ClientConfigEvent;
|
||||
import com.acgist.taoyao.signal.event.ClientOnlineEvent;
|
||||
import com.acgist.taoyao.signal.event.MediaClientRegisterEvent;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
|
||||
import com.acgist.taoyao.signal.service.SecurityService;
|
||||
@@ -20,7 +23,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 终端注册信令
|
||||
* 如果需要验证终端授权自行实现
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@@ -40,6 +42,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
"temperature": 温度,
|
||||
"signal": 信号强度(0~100),
|
||||
"battery": 电池电量(0~100),
|
||||
"alarming": 是否发生告警(true|false),
|
||||
"charging": 是否正在充电(true|false),
|
||||
"recording": 是否正在录像(true|false),
|
||||
"status": {更多状态},
|
||||
@@ -47,8 +50,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
}
|
||||
""",
|
||||
flow = {
|
||||
"终端->信令服务->终端",
|
||||
"终端->信令服务-[终端上线])终端"
|
||||
"终端=>信令服务->终端",
|
||||
"终端=>信令服务-[终端配置]>终端",
|
||||
"终端=>信令服务-[终端上线])终端"
|
||||
}
|
||||
)
|
||||
public class ClientRegisterProtocol extends ProtocolClientAdapter {
|
||||
@@ -56,14 +60,10 @@ public class ClientRegisterProtocol extends ProtocolClientAdapter {
|
||||
public static final String SIGNAL = "client::register";
|
||||
|
||||
private final SecurityService securityService;
|
||||
private final ClientConfigProtocol clientConfigProtocol;
|
||||
private final ClientOnlineProtocol clientOnlineProtocol;
|
||||
|
||||
public ClientRegisterProtocol(SecurityService securityService, ClientConfigProtocol clientConfigProtocol, ClientOnlineProtocol clientOnlineProtocol) {
|
||||
public ClientRegisterProtocol(SecurityService securityService) {
|
||||
super("终端注册信令", SIGNAL);
|
||||
this.securityService = securityService;
|
||||
this.clientConfigProtocol = clientConfigProtocol;
|
||||
this.clientOnlineProtocol = clientOnlineProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,12 +71,11 @@ public class ClientRegisterProtocol extends ProtocolClientAdapter {
|
||||
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();
|
||||
CloseableUtils.close(oldClient);
|
||||
}
|
||||
log.info("终端注册:{}", clientId);
|
||||
client.authorize(clientId);
|
||||
@@ -85,20 +84,17 @@ public class ClientRegisterProtocol extends ProtocolClientAdapter {
|
||||
throw MessageCodeException.of(MessageCode.CODE_3401, "注册失败");
|
||||
}
|
||||
final ClientType clientType = ClientType.of(MapUtils.get(body, Constant.CLIENT_TYPE));
|
||||
// 推送消息
|
||||
final Message registerResponse = message.cloneWithoutBody();
|
||||
registerResponse.setBody(Map.of(Constant.INDEX, this.idService.buildClientIndex()));
|
||||
client.push(registerResponse);
|
||||
// 下发配置
|
||||
client.push(this.clientConfigProtocol.build(clientType));
|
||||
// 终端状态
|
||||
final ClientStatus status = this.buildStatus(clientId, clientType, client, body);
|
||||
// 上线事件
|
||||
this.clientManager.broadcast(
|
||||
clientId,
|
||||
this.clientOnlineProtocol.build(status)
|
||||
);
|
||||
// 媒体服务终端注册
|
||||
// 注册响应消息
|
||||
final Message response = message.cloneWithoutBody();
|
||||
response.setBody(Map.of(Constant.INDEX, this.idService.buildClientIndex()));
|
||||
client.push(response);
|
||||
// 设置终端状态
|
||||
this.buildStatus(clientId, clientType, client, body);
|
||||
// 终端配置事件
|
||||
this.publishEvent(new ClientConfigEvent(client));
|
||||
// 终端上线事件
|
||||
this.publishEvent(new ClientOnlineEvent(client));
|
||||
// 媒体服务注册事件
|
||||
if(clientType == ClientType.MEDIA) {
|
||||
this.publishEvent(new MediaClientRegisterEvent(client));
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ import com.acgist.taoyao.signal.protocol.ProtocolControlAdapter;
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
memo = "状态通过心跳回传",
|
||||
flow = {
|
||||
"信令服务->终端",
|
||||
"终端->信令服务->终端"
|
||||
},
|
||||
memo = "状态通过心跳回传"
|
||||
}
|
||||
)
|
||||
public class ControlRecordProtocol extends ProtocolControlAdapter {
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.acgist.taoyao.signal.protocol.room;
|
||||
|
||||
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.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 房间广播信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
...
|
||||
}
|
||||
""",
|
||||
flow = "终端->信令服务->终端"
|
||||
)
|
||||
public class RoomBroadcastProtocol extends ProtocolRoomAdapter {
|
||||
|
||||
public static final String SIGNAL = "room::broadcast";
|
||||
|
||||
protected RoomBroadcastProtocol() {
|
||||
super("房间广播信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
room.broadcast(client, message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,6 +31,7 @@ public class RoomCloseProtocol extends ProtocolRoomAdapter {
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
room.close();
|
||||
this.clientManager.broadcast(message);
|
||||
// TODO:释放房间
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,66 @@
|
||||
package com.acgist.taoyao.signal.protocol.room;
|
||||
|
||||
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.signal.client.Client;
|
||||
import com.acgist.taoyao.signal.client.ClientType;
|
||||
import com.acgist.taoyao.signal.event.RoomLeaveEvent;
|
||||
import com.acgist.taoyao.signal.party.media.Room;
|
||||
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
|
||||
|
||||
/**
|
||||
* 离开房间信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class RoomLeaveProtocol {
|
||||
@Protocol
|
||||
@Description(
|
||||
body = """
|
||||
{
|
||||
"clientId": "离开终端ID"
|
||||
}
|
||||
""",
|
||||
flow = {
|
||||
"终端->信令服务-)终端",
|
||||
"终端-[关闭终端]>信令服务-)终端",
|
||||
}
|
||||
)
|
||||
public class RoomLeaveProtocol extends ProtocolRoomAdapter implements ApplicationListener<RoomLeaveEvent> {
|
||||
|
||||
public static final String SIGNAL = "room::leave";
|
||||
|
||||
public RoomLeaveProtocol() {
|
||||
super("离开房间信令", SIGNAL);
|
||||
}
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void onApplicationEvent(RoomLeaveEvent event) {
|
||||
this.leave(event.getRoom(), event.getClient());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
|
||||
// 离开房间后会发布事件
|
||||
room.leave(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* 离开房间
|
||||
*
|
||||
* @param room 房间
|
||||
* @param client 终端
|
||||
*/
|
||||
private void leave(Room room, Client client) {
|
||||
final Message leaveMessage = this.build(Map.of(Constant.CLIENT_ID, client.clientId()));
|
||||
room.broadcast(client, leaveMessage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user