[+] 分辨率调整
This commit is contained in:
@@ -8,7 +8,6 @@
|
|||||||
|:--|:--|:--|
|
|:--|:--|:--|
|
||||||
|taoyao-client-web|终端示例|Web终端示例|
|
|taoyao-client-web|终端示例|Web终端示例|
|
||||||
|taoyao-client-android|终端示例|安卓终端示例|
|
|taoyao-client-android|终端示例|安卓终端示例|
|
||||||
|taoyao-client-desktop|终端示例|桌面终端示例|
|
|
||||||
|taoyao-media-server|媒体服务|Mediasoup媒体服务|
|
|taoyao-media-server|媒体服务|Mediasoup媒体服务|
|
||||||
|taoyao-signal-server|信令服务|信令业务逻辑|
|
|taoyao-signal-server|信令服务|信令业务逻辑|
|
||||||
|
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ firewall-cmd --zone=public --add-port=9999/tcp --permanent
|
|||||||
# 信令服务(WebSocket)
|
# 信令服务(WebSocket)
|
||||||
firewall-cmd --zone=public --add-port=8888/tcp --permanent
|
firewall-cmd --zone=public --add-port=8888/tcp --permanent
|
||||||
# 媒体服务(控制):只暴露给信令服务
|
# 媒体服务(控制):只暴露给信令服务
|
||||||
firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="4443" accept"
|
firewall-cmd --zone=public --add-rich-rule="rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="4443" accept" --permanent
|
||||||
# 媒体服务(数据)
|
# 媒体服务(数据)
|
||||||
firewall-cmd --zone=public --add-port=40000-49999/udp --permanent
|
firewall-cmd --zone=public --add-port=40000-49999/udp --permanent
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ firewall-cmd --list-ports
|
|||||||
|
|
||||||
# 删除端口
|
# 删除端口
|
||||||
#firewall-cmd --zone=public --remove-port=8443/tcp --permanent
|
#firewall-cmd --zone=public --remove-port=8443/tcp --permanent
|
||||||
firewall-cmd --permanent --remove-rich-rule="rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="4443" accept"
|
#firewall-cmd --zone=public --remove-rich-rule="rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="4443" accept" --permanent
|
||||||
#firewall-cmd --zone=public --remove-port=9999/tcp --permanent
|
#firewall-cmd --zone=public --remove-port=9999/tcp --permanent
|
||||||
#firewall-cmd --zone=public --remove-port=8888/tcp --permanent
|
#firewall-cmd --zone=public --remove-port=8888/tcp --permanent
|
||||||
#firewall-cmd --zone=public --remove-port=40000-49999/udp --permanent
|
#firewall-cmd --zone=public --remove-port=40000-49999/udp --permanent
|
||||||
|
|||||||
@@ -31,26 +31,31 @@ public class MediaProperties {
|
|||||||
*/
|
*/
|
||||||
@Schema(title = "视频配置", description = "视频配置")
|
@Schema(title = "视频配置", description = "视频配置")
|
||||||
private MediaVideoProperties video;
|
private MediaVideoProperties video;
|
||||||
|
/**
|
||||||
|
* 4K视频
|
||||||
|
*/
|
||||||
|
@Schema(title = "4K视频", description = "4K视频")
|
||||||
|
private MediaVideoProperties udVideo;
|
||||||
|
/**
|
||||||
|
* 2K视频
|
||||||
|
*/
|
||||||
|
@Schema(title = "2K视频", description = "2K视频")
|
||||||
|
private MediaVideoProperties qdVideo;
|
||||||
/**
|
/**
|
||||||
* 超清视频
|
* 超清视频
|
||||||
*/
|
*/
|
||||||
@Schema(title = "超清视频", description = "超清视频")
|
@Schema(title = "超清视频", description = "超清视频")
|
||||||
private MediaVideoProperties mostVideo;
|
private MediaVideoProperties fdVideo;
|
||||||
/**
|
/**
|
||||||
* 高清视频
|
* 高清视频
|
||||||
*/
|
*/
|
||||||
@Schema(title = "高清视频", description = "高清视频")
|
@Schema(title = "高清视频", description = "高清视频")
|
||||||
private MediaVideoProperties highVideo;
|
private MediaVideoProperties hdVideo;
|
||||||
/**
|
/**
|
||||||
* 标清视频
|
* 标清视频
|
||||||
*/
|
*/
|
||||||
@Schema(title = "标清视频", description = "标清视频")
|
@Schema(title = "标清视频", description = "标清视频")
|
||||||
private MediaVideoProperties normVideo;
|
private MediaVideoProperties sdVideo;
|
||||||
/**
|
|
||||||
* 流畅视频
|
|
||||||
*/
|
|
||||||
@Schema(title = "流畅视频", description = "流畅视频")
|
|
||||||
private MediaVideoProperties flowVideo;
|
|
||||||
/**
|
/**
|
||||||
* 媒体服务配置
|
* 媒体服务配置
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class MediaVideoProperties {
|
|||||||
/**
|
/**
|
||||||
* 分辨率(画面大小)
|
* 分辨率(画面大小)
|
||||||
*/
|
*/
|
||||||
@Schema(title = "分辨率", description = "分辨率影响画面大小", example = "1920*1080|1280*720|480*360")
|
@Schema(title = "分辨率", description = "分辨率影响画面大小", example = "1920*1080|1280*720")
|
||||||
private String resolution;
|
private String resolution;
|
||||||
/**
|
/**
|
||||||
* 宽度
|
* 宽度
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ public class WebrtcProperties {
|
|||||||
* STUN服务器
|
* STUN服务器
|
||||||
*/
|
*/
|
||||||
@Schema(title = "STUN服务器", description = "STUN服务器")
|
@Schema(title = "STUN服务器", description = "STUN服务器")
|
||||||
private String[] stun;
|
private WebrtcStunProperties[] stun;
|
||||||
/**
|
/**
|
||||||
* TURN服务器
|
* TURN服务器
|
||||||
*/
|
*/
|
||||||
@Schema(title = "TURN服务器", description = "TURN服务器")
|
@Schema(title = "TURN服务器", description = "TURN服务器")
|
||||||
private String[] turn;
|
private WebrtcTurnProperties[] turn;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.acgist.taoyao.boot.property;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebRTC STUN配置
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Schema(title = "WebRTC STUN配置", description = "WebRTC STUN配置")
|
||||||
|
public class WebrtcStunProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机
|
||||||
|
*/
|
||||||
|
@Schema(title = "主机", description = "主机")
|
||||||
|
protected String host;
|
||||||
|
/**
|
||||||
|
* 端口
|
||||||
|
*/
|
||||||
|
@Schema(title = "端口", description = "端口")
|
||||||
|
protected Integer port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 完整地址
|
||||||
|
*/
|
||||||
|
@Schema(title = "完整地址", description = "完整地址")
|
||||||
|
public String getAddress() {
|
||||||
|
return "stun://" + this.host + ":" + this.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.acgist.taoyao.boot.property;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebRTC TURN配置
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Schema(title = "WebRTC TURN配置", description = "WebRTC TURN配置")
|
||||||
|
public class WebrtcTurnProperties extends WebrtcStunProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帐号
|
||||||
|
*/
|
||||||
|
@Schema(title = "帐号", description = "帐号")
|
||||||
|
private String username;
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
@Schema(title = "密码", description = "密码")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 完整地址
|
||||||
|
*/
|
||||||
|
@Schema(title = "完整地址", description = "完整地址")
|
||||||
|
public String getAddress() {
|
||||||
|
return "turn://" + this.host + ":" + this.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package com.acgist.taoyao.boot.utils;
|
||||||
|
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP工具
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public final class HTTPUtils {
|
||||||
|
|
||||||
|
private HTTPUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return HTTPClient
|
||||||
|
*/
|
||||||
|
public static final HttpClient newClient() {
|
||||||
|
return HttpClient
|
||||||
|
.newBuilder()
|
||||||
|
.sslContext(buildSSLContext())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSLContext
|
||||||
|
*
|
||||||
|
* @return {@link SSLContext}
|
||||||
|
*/
|
||||||
|
private static final SSLContext buildSSLContext() {
|
||||||
|
try {
|
||||||
|
// SSL协议:SSL、SSLv2、SSLv3、TLS、TLSv1、TLSv1.1、TLSv1.2、TLSv1.3
|
||||||
|
final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
|
||||||
|
sslContext.init(null, new X509TrustManager[] { TaoyaoTrustManager.INSTANCE }, new SecureRandom());
|
||||||
|
return sslContext;
|
||||||
|
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
||||||
|
log.error("新建SSLContext异常", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return SSLContext.getDefault();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
log.error("新建SSLContext异常", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 证书验证
|
||||||
|
*
|
||||||
|
* @author acgist
|
||||||
|
*/
|
||||||
|
public static class TaoyaoTrustManager implements X509TrustManager {
|
||||||
|
|
||||||
|
private static final TaoyaoTrustManager INSTANCE = new TaoyaoTrustManager();
|
||||||
|
|
||||||
|
private TaoyaoTrustManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
if(chain == null) {
|
||||||
|
throw new CertificateException("证书验证失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
if(chain == null) {
|
||||||
|
throw new CertificateException("证书验证失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
taoyao:
|
|
||||||
script:
|
|
||||||
media-reboot: pm2 restart taoyao-media-server
|
|
||||||
media-shutdown: pm2 stop taoyao-media-server
|
|
||||||
system-reboot: shutdown -s -t 0
|
|
||||||
system-shutdown: shutdown -r -t 0
|
|
||||||
platform-reboot: net stop taoyao-signal-server & net start taoyao-signal-server
|
|
||||||
platform-shutdown: net stop taoyao-signal-server
|
|
||||||
@@ -20,7 +20,7 @@ server:
|
|||||||
# context-path: /taoyao
|
# context-path: /taoyao
|
||||||
spring:
|
spring:
|
||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: dev
|
||||||
application:
|
application:
|
||||||
name: taoyao-signal-server
|
name: taoyao-signal-server
|
||||||
servlet:
|
servlet:
|
||||||
@@ -68,30 +68,36 @@ taoyao:
|
|||||||
bitrate: 1200
|
bitrate: 1200
|
||||||
frame-rate: 24
|
frame-rate: 24
|
||||||
resolution: 1920*1080
|
resolution: 1920*1080
|
||||||
# 超清视频
|
# 4K:UD=UHD=4K
|
||||||
most-video:
|
ud-video:
|
||||||
|
format: H264
|
||||||
|
bitrate: 1600
|
||||||
|
frame-rate: 30
|
||||||
|
resolution: 4096*2160
|
||||||
|
# 2K:QD=QHD=2K
|
||||||
|
qd-video:
|
||||||
|
format: H264
|
||||||
|
bitrate: 1600
|
||||||
|
frame-rate: 30
|
||||||
|
resolution: 2560*1440
|
||||||
|
# 超清视频:FD=FHD=1080P
|
||||||
|
fd-video:
|
||||||
format: H264
|
format: H264
|
||||||
bitrate: 1200
|
bitrate: 1200
|
||||||
frame-rate: 24
|
frame-rate: 24
|
||||||
resolution: 1920*1080
|
resolution: 1920*1080
|
||||||
# 高清视频
|
# 高清视频:HD=720P
|
||||||
high-video:
|
hd-video:
|
||||||
format: H264
|
format: H264
|
||||||
bitrate: 1000
|
bitrate: 1000
|
||||||
frame-rate: 18
|
frame-rate: 18
|
||||||
resolution: 1280*720
|
resolution: 1280*720
|
||||||
# 标清视频
|
# 标清视频:SD=480P
|
||||||
norm-video:
|
sd-video:
|
||||||
format: H264
|
format: H264
|
||||||
bitrate: 800
|
bitrate: 800
|
||||||
frame-rate: 16
|
frame-rate: 16
|
||||||
resolution: 720*480
|
resolution: 720*480
|
||||||
# 流畅视频
|
|
||||||
flow-video:
|
|
||||||
format: H264
|
|
||||||
bitrate: 600
|
|
||||||
frame-rate: 16
|
|
||||||
resolution: 640*480
|
|
||||||
# 媒体服务配置
|
# 媒体服务配置
|
||||||
media-server-list:
|
media-server-list:
|
||||||
- name: media-local-a
|
- name: media-local-a
|
||||||
@@ -120,22 +126,26 @@ taoyao:
|
|||||||
thread-min: 4
|
thread-min: 4
|
||||||
thread-max: 128
|
thread-max: 128
|
||||||
thread-name-prefix: ${spring.application.name}-signal-
|
thread-name-prefix: ${spring.application.name}-signal-
|
||||||
keep-alive-time: 60
|
keep-alive-time: 60000
|
||||||
buffer-size: 2048
|
buffer-size: 2048
|
||||||
# WebRTC配置
|
# WebRTC配置:没有P2P所以不会用到
|
||||||
webrtc:
|
webrtc:
|
||||||
# STUN服务
|
# STUN服务
|
||||||
stun:
|
stun:
|
||||||
- stun:stun1.l.google.com:19302
|
- host: stun1.l.google.com
|
||||||
- stun:stun2.l.google.com:19302
|
port: 19302
|
||||||
- stun:stun3.l.google.com:19302
|
- host: stun2.l.google.com
|
||||||
- stun:stun4.l.google.com:19302
|
port: 19302
|
||||||
# TURN服务
|
# TURN服务:需要自己搭建coturn
|
||||||
turn:
|
turn:
|
||||||
- turn:192.168.8.110:3478
|
- host: 192.168.8.110
|
||||||
- turn:192.168.8.111:3478
|
port: 3478
|
||||||
- turn:192.168.8.112:3478
|
username: taoyao
|
||||||
- turn:192.168.8.113:3478
|
password: taoyao
|
||||||
|
- host: 192.168.8.111
|
||||||
|
port: 3478
|
||||||
|
username: taoyao
|
||||||
|
password: taoyao
|
||||||
# 安全配置
|
# 安全配置
|
||||||
security:
|
security:
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -145,6 +155,7 @@ taoyao:
|
|||||||
password: taoyao
|
password: taoyao
|
||||||
# 定时任务
|
# 定时任务
|
||||||
scheduled:
|
scheduled:
|
||||||
|
media: 0 * * * * ?
|
||||||
client: 0 * * * * ?
|
client: 0 * * * * ?
|
||||||
# 脚本配置
|
# 脚本配置
|
||||||
script:
|
script:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import com.acgist.taoyao.signal.event.client.ClientCloseEvent;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 会话管理
|
* 终端管理
|
||||||
*
|
*
|
||||||
* @author acgist
|
* @author acgist
|
||||||
*/
|
*/
|
||||||
@@ -30,7 +30,7 @@ public class ClientManager {
|
|||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 会话列表
|
* 终端列表
|
||||||
*/
|
*/
|
||||||
private List<Client> clients = new CopyOnWriteArrayList<>();
|
private List<Client> clients = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
@@ -40,6 +40,8 @@ public class ClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 终端打开加入管理
|
||||||
|
*
|
||||||
* @param client 终端
|
* @param client 终端
|
||||||
*/
|
*/
|
||||||
public void open(Client client) {
|
public void open(Client client) {
|
||||||
@@ -118,7 +120,7 @@ public class ClientManager {
|
|||||||
/**
|
/**
|
||||||
* @param sn 终端标识
|
* @param sn 终端标识
|
||||||
*
|
*
|
||||||
* @return 终端会话
|
* @return 终端列表
|
||||||
*/
|
*/
|
||||||
public List<Client> clients(String sn) {
|
public List<Client> clients(String sn) {
|
||||||
return this.clients().stream()
|
return this.clients().stream()
|
||||||
@@ -127,7 +129,7 @@ public class ClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 所有终端会话
|
* @return 所有终端列表
|
||||||
*/
|
*/
|
||||||
public List<Client> clients() {
|
public List<Client> clients() {
|
||||||
return this.clients.stream()
|
return this.clients.stream()
|
||||||
@@ -148,7 +150,7 @@ public class ClientManager {
|
|||||||
/**
|
/**
|
||||||
* @param sn 终端标识
|
* @param sn 终端标识
|
||||||
*
|
*
|
||||||
* @return 终端状态
|
* @return 终端状态列表
|
||||||
*/
|
*/
|
||||||
public List<ClientStatus> status(String sn) {
|
public List<ClientStatus> status(String sn) {
|
||||||
return this.clients(sn).stream()
|
return this.clients(sn).stream()
|
||||||
@@ -157,7 +159,7 @@ public class ClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 所有终端状态
|
* @return 所有终端状态列表
|
||||||
*/
|
*/
|
||||||
public List<ClientStatus> status() {
|
public List<ClientStatus> status() {
|
||||||
return this.clients().stream()
|
return this.clients().stream()
|
||||||
@@ -168,7 +170,7 @@ public class ClientManager {
|
|||||||
/**
|
/**
|
||||||
* 发送消息
|
* 发送消息
|
||||||
*
|
*
|
||||||
* @param instance 会话实例
|
* @param instance 终端实例
|
||||||
* @param message 消息
|
* @param message 消息
|
||||||
*/
|
*/
|
||||||
public void send(AutoCloseable instance, Message message) {
|
public void send(AutoCloseable instance, Message message) {
|
||||||
@@ -181,9 +183,9 @@ public class ClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭会话
|
* 关闭终端
|
||||||
*
|
*
|
||||||
* @param instance 会话实例
|
* @param instance 终端实例
|
||||||
*/
|
*/
|
||||||
public void close(AutoCloseable instance) {
|
public void close(AutoCloseable instance) {
|
||||||
final Client client = this.client(instance);
|
final Client client = this.client(instance);
|
||||||
@@ -195,7 +197,7 @@ public class ClientManager {
|
|||||||
instance.close();
|
instance.close();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("关闭会话异常", e);
|
log.error("关闭终端异常", e);
|
||||||
} finally {
|
} finally {
|
||||||
if(client != null) {
|
if(client != null) {
|
||||||
// 移除管理
|
// 移除管理
|
||||||
@@ -207,15 +209,15 @@ public class ClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时关闭超时会话
|
* 定时关闭超时终端
|
||||||
*/
|
*/
|
||||||
private void closeTimeout() {
|
private void closeTimeout() {
|
||||||
log.debug("定时关闭超时会话");
|
log.debug("定时关闭超时终端");
|
||||||
this.clients.stream()
|
this.clients.stream()
|
||||||
.filter(v -> !v.authorized())
|
.filter(v -> !v.authorized())
|
||||||
.filter(v -> v.timeout(this.taoyaoProperties.getTimeout()))
|
.filter(v -> v.timeout(this.taoyaoProperties.getTimeout()))
|
||||||
.forEach(v -> {
|
.forEach(v -> {
|
||||||
log.debug("关闭超时会话:{}", v);
|
log.debug("关闭超时终端:{}", v);
|
||||||
this.close(v);
|
this.close(v);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class SocketSignal {
|
|||||||
this.socketProperties.getThreadMin(),
|
this.socketProperties.getThreadMin(),
|
||||||
this.socketProperties.getThreadMax(),
|
this.socketProperties.getThreadMax(),
|
||||||
this.socketProperties.getKeepAliveTime(),
|
this.socketProperties.getKeepAliveTime(),
|
||||||
TimeUnit.SECONDS,
|
TimeUnit.MILLISECONDS,
|
||||||
new LinkedBlockingQueue<>(this.socketProperties.getQueueSize()),
|
new LinkedBlockingQueue<>(this.socketProperties.getQueueSize()),
|
||||||
this.newThreadFactory()
|
this.newThreadFactory()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
package com.acgist.taoyao.signal.media;
|
package com.acgist.taoyao.signal.media;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.WebSocket;
|
import java.net.http.WebSocket;
|
||||||
import java.net.http.WebSocket.Listener;
|
import java.net.http.WebSocket.Listener;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionStage;
|
import java.util.concurrent.CompletionStage;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.net.ssl.SSLContext;
|
import java.util.concurrent.TimeoutException;
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -33,6 +27,7 @@ import com.acgist.taoyao.boot.model.MessageCode;
|
|||||||
import com.acgist.taoyao.boot.model.MessageCodeException;
|
import com.acgist.taoyao.boot.model.MessageCodeException;
|
||||||
import com.acgist.taoyao.boot.property.MediaServerProperties;
|
import com.acgist.taoyao.boot.property.MediaServerProperties;
|
||||||
import com.acgist.taoyao.boot.property.TaoyaoProperties;
|
import com.acgist.taoyao.boot.property.TaoyaoProperties;
|
||||||
|
import com.acgist.taoyao.boot.utils.HTTPUtils;
|
||||||
import com.acgist.taoyao.boot.utils.JSONUtils;
|
import com.acgist.taoyao.boot.utils.JSONUtils;
|
||||||
import com.acgist.taoyao.signal.protocol.Protocol;
|
import com.acgist.taoyao.signal.protocol.Protocol;
|
||||||
import com.acgist.taoyao.signal.protocol.ProtocolManager;
|
import com.acgist.taoyao.signal.protocol.ProtocolManager;
|
||||||
@@ -105,6 +100,19 @@ public class MediaClient {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心跳
|
||||||
|
*/
|
||||||
|
public void heartbeat() {
|
||||||
|
final CompletableFuture<WebSocket> future = this.webSocket.sendPing(ByteBuffer.allocate(0));
|
||||||
|
try {
|
||||||
|
log.debug("心跳:{}", this.name);
|
||||||
|
future.get(this.taoyaoProperties.getTimeout(), TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
log.error("心跳异常:{}", this.name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接WebSocket通道
|
* 连接WebSocket通道
|
||||||
*/
|
*/
|
||||||
@@ -112,14 +120,12 @@ public class MediaClient {
|
|||||||
final URI uri = URI.create(this.mediaServerProperties.getAddress());
|
final URI uri = URI.create(this.mediaServerProperties.getAddress());
|
||||||
log.info("连接媒体服务:{}", uri);
|
log.info("连接媒体服务:{}", uri);
|
||||||
try {
|
try {
|
||||||
HttpClient
|
final WebSocket webSocket = HTTPUtils.newClient()
|
||||||
.newBuilder()
|
|
||||||
.sslContext(buildSSLContext())
|
|
||||||
.build()
|
|
||||||
.newWebSocketBuilder()
|
.newWebSocketBuilder()
|
||||||
.connectTimeout(Duration.ofMillis(this.taoyaoProperties.getTimeout()))
|
.connectTimeout(Duration.ofMillis(this.taoyaoProperties.getTimeout()))
|
||||||
.buildAsync(uri, new MessageListener())
|
.buildAsync(uri, new MessageListener())
|
||||||
.get();
|
.get();
|
||||||
|
log.info("连接媒体服务成功:{}", webSocket);
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
log.error("连接媒体服务异常:{}", uri, e);
|
log.error("连接媒体服务异常:{}", uri, e);
|
||||||
this.taskScheduler.schedule(
|
this.taskScheduler.schedule(
|
||||||
@@ -304,59 +310,4 @@ public class MediaClient {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* SSLContext
|
|
||||||
*
|
|
||||||
* @return {@link SSLContext}
|
|
||||||
*/
|
|
||||||
private static final SSLContext buildSSLContext() {
|
|
||||||
try {
|
|
||||||
// SSL协议:SSL、SSLv2、SSLv3、TLS、TLSv1、TLSv1.1、TLSv1.2、TLSv1.3
|
|
||||||
final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
|
|
||||||
sslContext.init(null, new X509TrustManager[] { TaoyaoTrustManager.INSTANCE }, new SecureRandom());
|
|
||||||
return sslContext;
|
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
|
||||||
log.error("新建SSLContext异常", e);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return SSLContext.getDefault();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
log.error("新建SSLContext异常", e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 证书验证
|
|
||||||
*
|
|
||||||
* @author acgist
|
|
||||||
*/
|
|
||||||
public static class TaoyaoTrustManager implements X509TrustManager {
|
|
||||||
|
|
||||||
private static final TaoyaoTrustManager INSTANCE = new TaoyaoTrustManager();
|
|
||||||
|
|
||||||
private TaoyaoTrustManager() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return new X509Certificate[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
|
||||||
if(chain == null) {
|
|
||||||
throw new CertificateException("证书验证失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
|
||||||
if(chain == null) {
|
|
||||||
throw new CertificateException("证书验证失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
import com.acgist.taoyao.boot.annotation.Manager;
|
import com.acgist.taoyao.boot.annotation.Manager;
|
||||||
import com.acgist.taoyao.boot.property.MediaProperties;
|
import com.acgist.taoyao.boot.property.MediaProperties;
|
||||||
@@ -30,6 +31,11 @@ public class MediaClientManager {
|
|||||||
*/
|
*/
|
||||||
private Map<String, MediaClient> clientMap = new ConcurrentHashMap<>();
|
private Map<String, MediaClient> clientMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Scheduled(cron = "${taoyao.scheduled.media:0 * * * * ?}")
|
||||||
|
public void scheduled() {
|
||||||
|
this.heartbeat();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载媒体服务终端
|
* 加载媒体服务终端
|
||||||
*/
|
*/
|
||||||
@@ -53,4 +59,13 @@ public class MediaClientManager {
|
|||||||
return this.clientMap.get(name);
|
return this.clientMap.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心跳
|
||||||
|
*/
|
||||||
|
private void heartbeat() {
|
||||||
|
this.clientMap.forEach((k, v) -> {
|
||||||
|
v.heartbeat();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user