[+] 协议
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.acgist.taoyao.config;
|
||||
|
||||
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;
|
||||
|
||||
@@ -22,6 +23,7 @@ public class TaoyaoAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "taoyao.security", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityInterceptor securityInterceptor() {
|
||||
return new SecurityInterceptor();
|
||||
|
||||
@@ -7,8 +7,12 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.acgist.taoyao.boot.config.MediaProperties;
|
||||
import com.acgist.taoyao.boot.config.WebrtcProperties;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
@@ -28,14 +32,16 @@ public class ConfigController {
|
||||
|
||||
@Operation(summary = "媒体配置", description = "媒体配置")
|
||||
@GetMapping("/media")
|
||||
public MediaProperties media() {
|
||||
return this.mediaProperties;
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = MediaProperties.class)))
|
||||
public Message media() {
|
||||
return Message.success(this.mediaProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "WebRTC配置", description = "WebRTC配置")
|
||||
@GetMapping("/webrtc")
|
||||
public WebrtcProperties webrtc() {
|
||||
return this.webrtcProperties;
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = WebrtcProperties.class)))
|
||||
public Message webrtc() {
|
||||
return Message.success(this.webrtcProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ taoyao:
|
||||
record:
|
||||
storage: /data/record
|
||||
security:
|
||||
enabled: true
|
||||
realm: taoyao
|
||||
permit: /v3/api-docs/,/swagger-ui/,/favicon.ico,/error
|
||||
username: taoyao
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** 桃夭WebRTC终端核心功能 */
|
||||
/** 配置 */
|
||||
const config = {
|
||||
const taoyaoConfig = {
|
||||
// 当前终端SN
|
||||
sn: 'taoyao',
|
||||
// 信令授权
|
||||
@@ -86,24 +86,26 @@ function Taoyao(
|
||||
let audioDevice = false;
|
||||
let videoDevice = false;
|
||||
list.forEach(v => {
|
||||
console.log('终端媒体设备', v.kind, v.label);
|
||||
console.debug('终端媒体设备', v.kind, v.label);
|
||||
if(v.kind === 'audioinput') {
|
||||
audioDevice = true;
|
||||
} else if(v.kind === 'videoinput') {
|
||||
videoDevice = true;
|
||||
} else {
|
||||
console.debug('没有适配设备', v.kind, v.label);
|
||||
}
|
||||
});
|
||||
if(!audioDevice) {
|
||||
console.log('终端没有音频输入设备');
|
||||
console.warn('终端没有音频输入设备');
|
||||
self.audioEnabled = false;
|
||||
}
|
||||
if(!videoDevice) {
|
||||
console.log('终端没有视频输入设备');
|
||||
console.warn('终端没有视频输入设备');
|
||||
self.videoEnabled = false;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log('获取终端设备失败', e);
|
||||
console.error('获取终端设备失败', e);
|
||||
self.videoEnabled = false;
|
||||
self.videoEnabled = false;
|
||||
});
|
||||
@@ -120,19 +122,31 @@ function Taoyao(
|
||||
xhr.responseType = mime;
|
||||
xhr.send(data);
|
||||
xhr.onload = function() {
|
||||
let response = xhr.response;
|
||||
if(xhr.readyState === 4 && xhr.status === 200) {
|
||||
resolve(xhr.response);
|
||||
if(response.code === '0000') {
|
||||
resolve(response.body);
|
||||
} else {
|
||||
reject(response.body || response);
|
||||
}
|
||||
} else {
|
||||
reject(xhr.response);
|
||||
reject(response.body || response);
|
||||
}
|
||||
}
|
||||
xhr.onerror = reject;
|
||||
} else {
|
||||
xhr.send(data);
|
||||
let response;
|
||||
try {
|
||||
response = JSON.parse(xhr.response);
|
||||
} catch(e) {
|
||||
console.error('响应解析失败', xhr);
|
||||
response = xhr.response;
|
||||
}
|
||||
if(xhr.readyState === 4 && xhr.status === 200) {
|
||||
resolve(JSON.parse(xhr.response));
|
||||
resolve(response.body || response);
|
||||
} else {
|
||||
reject(JSON.parse(xhr.response));
|
||||
reject(response.body || response);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -141,13 +155,13 @@ function Taoyao(
|
||||
this.configMedia = function(audio = {}, video = {}) {
|
||||
this.audioConfig = {...this.audioConfig, ...audio};
|
||||
this.videoCofnig = {...this.videoCofnig, ...video};
|
||||
console.log('终端媒体配置', this.audioConfig, this.videoConfig);
|
||||
console.debug('终端媒体配置', this.audioConfig, this.videoConfig);
|
||||
};
|
||||
/** WebRTC配置 */
|
||||
this.configWebrtc = function(config = {}) {
|
||||
this.webSocket = config.signalAddress;
|
||||
this.iceServer = config.stun;
|
||||
console.log('WebRTC配置', this.webSocket, this.iceServer);
|
||||
console.debug('WebRTC配置', this.webSocket, this.iceServer);
|
||||
};
|
||||
/** 信令通道 */
|
||||
this.buildChannel = function(callback) {
|
||||
@@ -160,7 +174,7 @@ function Taoyao(
|
||||
};
|
||||
/** 本地媒体 */
|
||||
this.buildLocalMedia = function() {
|
||||
console.log("获取终端媒体:", this.audioConfig, this.videoConfig);
|
||||
console.debug("获取终端媒体:", this.audioConfig, this.videoConfig);
|
||||
let self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
@@ -190,6 +204,14 @@ function Taoyao(
|
||||
}
|
||||
await this.localVideo.play();
|
||||
};
|
||||
/** 关闭:关闭媒体 */
|
||||
this.close = function() {
|
||||
// TODO:释放资源
|
||||
};
|
||||
/** 关机:关闭媒体、关闭信令 */
|
||||
this.shutdown = function() {
|
||||
this.close();
|
||||
}
|
||||
/** 媒体 */
|
||||
/** 视频 */
|
||||
};
|
||||
@@ -197,7 +219,7 @@ function Taoyao(
|
||||
const protocol = {
|
||||
pid: {
|
||||
/** 心跳 */
|
||||
heartbeat: 1000,
|
||||
heartbeat: 2005,
|
||||
/** 注册 */
|
||||
register: 2000
|
||||
},
|
||||
@@ -240,7 +262,7 @@ const signalChannel = {
|
||||
/** 回调事件 */
|
||||
callbackMapping: new Map(),
|
||||
/** 心跳时间 */
|
||||
heartbeatTime: 10 * 1000,
|
||||
heartbeatTime: 30 * 1000,
|
||||
/** 心跳定时器 */
|
||||
heartbeatTimer: null,
|
||||
/** 防止重连 */
|
||||
@@ -250,9 +272,9 @@ const signalChannel = {
|
||||
/** 最小重连时间 */
|
||||
minReconnectionDelay: 5 * 1000,
|
||||
/** 最大重连时间 */
|
||||
maxReconnectionDelay: 5 * 60 * 1000,
|
||||
maxReconnectionDelay: 60 * 1000,
|
||||
/** 自动重连失败后重连时间增长倍数 */
|
||||
reconnectionDelayGrowFactor: 1.5,
|
||||
reconnectionDelayGrowFactor: 2,
|
||||
/** 关闭 */
|
||||
close: function() {
|
||||
clearTimeout(this.heartbeatTimer);
|
||||
@@ -262,10 +284,17 @@ const signalChannel = {
|
||||
let self = this;
|
||||
self.heartbeatTimer = setTimeout(function() {
|
||||
if (self.channel && self.channel.readyState == WebSocket.OPEN) {
|
||||
self.push(protocol.buildProtocol(config.sn, protocol.pid.heartbeat));
|
||||
self.push(protocol.buildProtocol(
|
||||
taoyaoConfig.sn,
|
||||
protocol.pid.heartbeat,
|
||||
{
|
||||
signal: 100,
|
||||
battery: 100
|
||||
}
|
||||
));
|
||||
self.heartbeat();
|
||||
} else {
|
||||
console.log('发送心跳失败', self.channel);
|
||||
console.warn('发送心跳失败', self.channel);
|
||||
}
|
||||
}, self.heartbeatTime);
|
||||
},
|
||||
@@ -283,7 +312,7 @@ const signalChannel = {
|
||||
}
|
||||
// 打开定时重连
|
||||
setTimeout(function() {
|
||||
console.log('信令通道重连', self.address);
|
||||
console.info('信令通道重连', self.address);
|
||||
self.connect(self.address, self.callback, true);
|
||||
self.lockReconnect = false;
|
||||
}, self.connectionTimeout);
|
||||
@@ -298,21 +327,21 @@ const signalChannel = {
|
||||
let self = this;
|
||||
this.address = address;
|
||||
this.callback = callback;
|
||||
console.log("连接信令通道", address);
|
||||
console.debug("连接信令通道", address);
|
||||
return new Promise((resolve, reject) => {
|
||||
self.channel = new WebSocket(address);
|
||||
self.channel.onopen = function(e) {
|
||||
console.log('信令通道打开', e);
|
||||
console.debug('信令通道打开', e);
|
||||
self.push(protocol.buildProtocol(
|
||||
config.sn,
|
||||
taoyaoConfig.sn,
|
||||
protocol.pid.register,
|
||||
{
|
||||
ip: null,
|
||||
mac: null,
|
||||
signal: 100,
|
||||
battery: 100,
|
||||
username: config.username,
|
||||
password: config.password
|
||||
username: taoyaoConfig.username,
|
||||
password: taoyaoConfig.password
|
||||
}
|
||||
));
|
||||
self.connectionTimeout = self.minReconnectionDelay
|
||||
@@ -320,7 +349,7 @@ const signalChannel = {
|
||||
resolve(e);
|
||||
};
|
||||
self.channel.onclose = function(e) {
|
||||
console.log('信令通道关闭', self.channel, e);
|
||||
console.error('信令通道关闭', self.channel, e);
|
||||
if(reconnection) {
|
||||
self.reconnect();
|
||||
}
|
||||
@@ -334,17 +363,20 @@ const signalChannel = {
|
||||
reject(e);
|
||||
};
|
||||
self.channel.onmessage = function(e) {
|
||||
console.log('信令消息', e.data);
|
||||
console.debug('信令消息', e.data);
|
||||
let data = JSON.parse(e.data);
|
||||
// 注册回调
|
||||
let done = false;
|
||||
if(callback) {
|
||||
callback(data);
|
||||
done = callback(data);
|
||||
}
|
||||
// 请求回调
|
||||
if(self.callbackMapping.has(data.header.id)) {
|
||||
self.callbackMapping.get(data.header.id)();
|
||||
self.callbackMapping.delete(data.header.id);
|
||||
}
|
||||
// 默认回调
|
||||
done || this.defaultCallback(data);
|
||||
};
|
||||
});
|
||||
},
|
||||
@@ -354,11 +386,22 @@ const signalChannel = {
|
||||
if(data && callback) {
|
||||
this.callbackMapping.set(data.header.id, callback);
|
||||
}
|
||||
// 发送请求
|
||||
if(data && data.header) {
|
||||
this.channel.send(JSON.stringify(data));
|
||||
} else {
|
||||
this.channel.send(data);
|
||||
}
|
||||
},
|
||||
/** 默认回调 */
|
||||
defaultCallback: function(data) {
|
||||
console.debug('没有适配信令消息默认处理', data);
|
||||
switch(data.header.pid) {
|
||||
case protocol.pid.heartbeat:
|
||||
break;
|
||||
case protocol.pid.register:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
/*
|
||||
|
||||
@@ -71,11 +71,12 @@
|
||||
switch(data.header.pid) {
|
||||
case protocol.pid.heartbeat:
|
||||
// 心跳
|
||||
break;
|
||||
return true;
|
||||
case protocol.pid.register:
|
||||
// 录制
|
||||
break;
|
||||
// 注册
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// 创建房间
|
||||
function create() {
|
||||
@@ -91,7 +92,7 @@
|
||||
}
|
||||
// 录制视频
|
||||
function record(e) {
|
||||
taoyao.push(protocol.buildProtocol(config.sn, protocol.pid.heartbeat), () => {
|
||||
taoyao.push(protocol.buildProtocol(taoyaoConfig.sn, protocol.pid.heartbeat), () => {
|
||||
classSwitch(e, 'active');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.acgist.taoyao.signal.protocol;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.acgist.taoyao.main.TaoyaoApplication;
|
||||
import com.acgist.taoyao.signal.protocol.platform.ScriptProtocol;
|
||||
import com.acgist.taoyao.test.annotation.TaoyaoTest;
|
||||
|
||||
@TaoyaoTest(classes = TaoyaoApplication.class)
|
||||
class ScriptProtocolTest {
|
||||
|
||||
@Autowired
|
||||
private ScriptProtocol scriptProtocol;
|
||||
|
||||
@Test
|
||||
void testScript() {
|
||||
assertDoesNotThrow(() -> {
|
||||
this.scriptProtocol.execute(null, Map.of("script", "netstat -ano"), null, null);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.acgist.taoyao.signal.protocol;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.acgist.taoyao.main.TaoyaoApplication;
|
||||
import com.acgist.taoyao.signal.protocol.platform.ShutdownProtocol;
|
||||
import com.acgist.taoyao.test.annotation.TaoyaoTest;
|
||||
|
||||
@TaoyaoTest(classes = TaoyaoApplication.class)
|
||||
class ShutdownProtocolTest {
|
||||
|
||||
@Autowired
|
||||
private ShutdownProtocol shutdownProtocol;
|
||||
|
||||
@Test
|
||||
void testShutdown() {
|
||||
assertDoesNotThrow(() -> {
|
||||
this.shutdownProtocol.execute("taoyao", null, null);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user