From cf476b842bbfc21004f0a2b3c2f00dbabc9dc5ae Mon Sep 17 00:00:00 2001 From: acgist <289547414@qq.com> Date: Sun, 20 Nov 2022 14:10:20 +0800 Subject: [PATCH] =?UTF-8?q?[*]=20=E4=BC=9A=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 20 +-- docs/flow/README.md | 33 ++++ docs/flow/WebRTC.png | Bin 0 -> 59950 bytes .../com/acgist/taoyao/meeting/Meeting.java | 3 + .../listener/MeetingCreateListener.java | 4 - .../listener/MeetingEnterListener.java | 42 +++++ .../resources/static/javascript/taoyao.js | 25 ++- .../src/main/resources/static/meeting.html | 6 +- taoyao-signal/README.md | 156 +++++++++++++----- .../event/meeting/MeetingEnterEvent.java | 22 +++ .../client/ClientRegisterListener.java | 2 + .../client/ClientHeartbeatProtocol.java | 2 +- .../meeting/MeetingEnterProtocol.java | 28 +++- taoyao-webrtc/README.md | 1 - 14 files changed, 274 insertions(+), 70 deletions(-) create mode 100644 docs/flow/README.md create mode 100644 docs/flow/WebRTC.png create mode 100644 taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingEnterListener.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/meeting/MeetingEnterEvent.java diff --git a/README.md b/README.md index c46d057..96350eb 100644 --- a/README.md +++ b/README.md @@ -44,34 +44,24 @@ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ``` -## 内网穿透 - -请用公共STUN/TURN服务或者自行搭建coturn服务。 - -> 只有公网Mesh架构才需要真正的内网穿透 - ## 直播 终端推流到服务端,由服务端分流。 ## 会议 -Mesh架构声音视频控制部分功能均在终端实现,同时不会实现终端录制、美颜、AI识别、变声、混音等等功能。 -MCU/SFU声音视频控制在服务端实现,如果没有终端订阅并且没有录制是不会对终端进行拉流。 - ### Mesh 流媒体点对点连接,不经过服务端。 +Mesh架构声音视频控制部分功能均在终端实现,同时不会实现录制、美颜、变声、混音等等功能。 + +> 需要使用STUN/TURN实现内网穿透(可以自己搭建coturn服务) ### MCU -终端推流到服务端,由服务端分流并且混音。 +终端推流到服务端,由服务端混音分流。 ### SFU -终端推流到服务端,由服务端分流没有混音。 +终端推流到服务端,由服务端直接分流。 -## TODO - -springdoc升级正式版本 -springboot升级正式版本 \ No newline at end of file diff --git a/docs/flow/README.md b/docs/flow/README.md new file mode 100644 index 0000000..c277ab8 --- /dev/null +++ b/docs/flow/README.md @@ -0,0 +1,33 @@ +# 流程 + +## WebRTC流程 + +发起响应 + +## SFU流程 + +媒体流 + +## MCU流程 + +媒体流 + +## Mesh流程 + +媒体流 + +## 直播流程 + +创建直播,终端操作。 + +## 会议流程 + +创建会议,终端操作 + +## 终端流程 + +注册下线 + +## 媒体控制 + +拉流发布 diff --git a/docs/flow/WebRTC.png b/docs/flow/WebRTC.png new file mode 100644 index 0000000000000000000000000000000000000000..555d2372ab60b2c9fef388b874b08964cf963889 GIT binary patch literal 59950 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 E0XZD_MgRZ+ literal 0 HcmV?d00001 diff --git a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/Meeting.java b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/Meeting.java index 1291fa9..3dde72a 100644 --- a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/Meeting.java +++ b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/Meeting.java @@ -39,6 +39,9 @@ public class Meeting { */ public void addSn(String sn) { synchronized (this.sns) { + if(this.sns.contains(sn)) { + return; + } this.sns.add(sn); } } diff --git a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingCreateListener.java b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingCreateListener.java index e270c08..1a6a1a3 100644 --- a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingCreateListener.java +++ b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingCreateListener.java @@ -9,7 +9,6 @@ import com.acgist.taoyao.boot.model.Message; import com.acgist.taoyao.meeting.Meeting; import com.acgist.taoyao.meeting.MeetingManager; import com.acgist.taoyao.signal.client.ClientSession; -import com.acgist.taoyao.signal.client.ClientSessionManager; import com.acgist.taoyao.signal.event.meeting.MeetingCreateEvent; import com.acgist.taoyao.signal.listener.ApplicationListenerAdapter; @@ -23,8 +22,6 @@ public class MeetingCreateListener extends ApplicationListenerAdapter { + + @Autowired + private MeetingManager meetingManager; + + @Override + public void onApplicationEvent(MeetingEnterEvent event) { + final Map body = event.getBody(); + final ClientSession session = event.getSession(); + final String sn = session.sn(); + final String id = (String) body.get("id"); + final Meeting meeting = this.meetingManager.meeting(id); + meeting.addSn(sn); + final Message message = event.getMessage(); + message.setBody(Map.of( + "id", meeting.getId(), + "sn", sn + )); + this.clientSessionManager.broadcast(sn, message); + } + +} diff --git a/taoyao-server/src/main/resources/static/javascript/taoyao.js b/taoyao-server/src/main/resources/static/javascript/taoyao.js index f2a6ffc..6f30618 100644 --- a/taoyao-server/src/main/resources/static/javascript/taoyao.js +++ b/taoyao-server/src/main/resources/static/javascript/taoyao.js @@ -1,6 +1,6 @@ /** 桃夭WebRTC终端核心功能 */ /** 兼容 */ -const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; +const RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; /** 默认音频配置 */ const defaultAudioConfig = { // 音量:0~1 @@ -39,6 +39,21 @@ const defaultVideoConfig = { // 选摄像头:user|left|right|environment facingMode: 'environment' } +/** 默认RTCPeerConnection配置 */ +const defaultRPCConfig = { + // ICE代理的服务器 + // iceServers: null, + // 证书 + // certificates: null, + // 传输通道绑定策略:balanced|max-compat|max-bundle + bundlePolicy: 'balanced', + // RTCP多路复用策略:require|negotiate + rtcpMuxPolicy: 'negotiate', + // ICE传输策略:all|relay + iceTransportPolicy: 'all' + // ICE候选个数 + // iceCandidatePoolSize: 10 +} /** 信令配置 */ const signalConfig = { /** 当前终端SN */ @@ -328,13 +343,9 @@ function TaoyaoClient( /** 设置媒体流 */ this.buildStream = async function(stream) { if(stream) { - if ('srcObject' in this.video) { - this.video.srcObject = stream; - } else { - this.video.src = URL.createObjectURL(stream);; - } + this.video.srcObject = stream; + await this.play(); } - await this.play(); return this; }; /** 设置音频流 */ diff --git a/taoyao-server/src/main/resources/static/meeting.html b/taoyao-server/src/main/resources/static/meeting.html index 6a447ef..e880553 100644 --- a/taoyao-server/src/main/resources/static/meeting.html +++ b/taoyao-server/src/main/resources/static/meeting.html @@ -62,9 +62,9 @@ this.taoyao .checkDevice() .buildChannel(this.callback) - //.buildLocalMedia() - //.then(stream => this.taoyao.buildLocalClient('local', stream)) - //.catch((e) => console.error('打开终端媒体失败', e)); + .buildLocalMedia() + .then(stream => this.taoyao.buildLocalClient('local', stream)) + .catch((e) => console.error('打开终端媒体失败', e)); }, beforeDestroy() { }, diff --git a/taoyao-signal/README.md b/taoyao-signal/README.md index 2c381c3..5ea43d8 100644 --- a/taoyao-signal/README.md +++ b/taoyao-signal/README.md @@ -102,7 +102,7 @@ #### 消息流程:终端->服务端-)终端 -终端注册成功响应同时[下发配置信令](#下发配置信令2004),并且广播[终端上线信令](#终端上线信令2002)。 +终端注册成功以后响应同时[下发配置信令](#下发配置信令2004),并且广播[终端上线信令](#终端上线信令2002)。 ### 终端关闭信令(2001) @@ -119,11 +119,11 @@ #### 消息流程:终端-)服务端 -广播[终端下线信令](#终端下线信令2003),同时释放所有相关资源(信令通道、媒体通道等等) +终端关闭以后广播[终端下线信令](#终端下线信令2003),同时释放所有相关资源(信令通道、媒体通道等等) ### 终端上线信令(2002) -服务端->终端:参考[终端注册信令](#终端注册信令) +#### 消息主体 ``` { @@ -131,9 +131,13 @@ } ``` +#### 消息流程:服务端->终端 + +参考[终端注册信令](#终端注册信令2000) + ### 终端下线信令(2003) -服务端->终端:参考[终端关闭信令](#关闭信令) +#### 消息主体 ``` { @@ -141,9 +145,13 @@ } ``` +#### 消息流程:服务端->终端 + +参考[终端关闭信令](#终端关闭信令2001) + ### 下发配置信令(2004) -服务端->终端:参考[注册信令](#注册信令) +#### 消息主体 ``` { @@ -153,56 +161,112 @@ } ``` +#### 消息流程:服务端->终端 + +参考[终端注册信令](#终端注册信令2000) + ### 心跳信令(2005) -心跳:响应 +#### 消息主体 ``` +# 请求 { "signal": "信号强度", "battery": "电池电量" } +# 响应 +{} +``` + +#### 消息流程:终端->服务端->终端 + +### 单播信令(2006) + +#### 消息主体 + +``` +# 请求 +{ + "to": "接收终端标识", + // 主体信息 +} +# 转发 +{ + // 主体信息 +} +``` + +#### 消息流程:终端->服务端->终端 + +终端转发信令到指定的终端 + +### 广播信令(2007) + +#### 消息主体 + +``` +# 请求 +{ + // 主体信息 +} +# 广播 +{ + // 主体信息 +} +``` + +#### 消息流程:终端->服务端-)终端 + +终端广播信令到所有的终端 + +### 终端状态信令(2998) + +#### 消息主体 + +``` +# 请求 +{ + "sn": "终端标识" +} +# 响应 +{ + "sn": "终端标识", + "ip": "IP地址", + "mac": "MAC地址", + "signal": "信号强度", + "battery": "电池电量" +} ``` -### 单播信令(2006) +#### 消息流程:终端->服务端->终端 -发送到指定的终端:删除`to`字段 - -``` -{ - "to": "接收终端标识", - // 主体信息 -} -``` - -### 广播信令(2007) - -发送到所有的终端:排除自己 - -``` -{ - // 主体信息 -} -``` - -### 终端状态信令(2998) - -返回指定终端状态(如果没有指定终端标识默认查询自己) - -``` -{ - "sn": "终端标识" -} -``` +响应指定终端状态(如果没有指定终端标识默认查询自己) ### 终端列表信令(2999) -返回所有终端状态列表 +#### 消息主体 ``` +# 请求 {} +# 响应 +[ + { + "sn": "终端标识", + "ip": "IP地址", + "mac": "MAC地址", + "signal": "信号强度", + "battery": "电池电量" + }, + ... +] ``` +#### 消息流程:终端->服务端->终端 + +响应所有终端状态列表 + ## 直播信令(3000~3999) ### 开启直播信令(3000) @@ -225,7 +289,7 @@ {} ---- { - "id": "会议标识" + "id": "会议标识", } ``` @@ -237,7 +301,23 @@ ### 进入会议信令(4002) -广播 +#### 消息主体 + +``` +# 请求 +{ + "id": "会议标识" +} +# 广播 +{ + "id": "会议标识", + "sn": "终端标识" +} +``` + +#### 消息流程:终端->服务端-)终端 + +终端进入会议,广播通知其他终端。 ### 离开会议信令(4003) diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/meeting/MeetingEnterEvent.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/meeting/MeetingEnterEvent.java new file mode 100644 index 0000000..570d636 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/meeting/MeetingEnterEvent.java @@ -0,0 +1,22 @@ +package com.acgist.taoyao.signal.event.meeting; + +import java.util.Map; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.client.ClientSession; +import com.acgist.taoyao.signal.event.ApplicationEventAdapter; + +/** + * 进入会议事件 + * + * @author acgist + */ +public class MeetingEnterEvent extends ApplicationEventAdapter { + + private static final long serialVersionUID = 1L; + + public MeetingEnterEvent(Map body, Message message, ClientSession session) { + super(body, message, session); + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/ClientRegisterListener.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/ClientRegisterListener.java index 192cfcb..cf7cabb 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/ClientRegisterListener.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/ClientRegisterListener.java @@ -16,6 +16,8 @@ import com.acgist.taoyao.signal.protocol.client.ClientOnlineProtocol; /** * 终端注册监听 * + * TODO:如果已经在会议、直播中,自动推流。 + * * @author acgist */ @Component diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java index 9e78aaf..8d5aa8c 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/ClientHeartbeatProtocol.java @@ -26,7 +26,7 @@ public class ClientHeartbeatProtocol extends ProtocolMapAdapter { @Override public void execute(String sn, Map body, Message message, ClientSession session) { - // 回应心跳 + // 响应心跳 session.push(message.cloneWidthoutBody()); // 设置状态 final ClientSessionStatus status = session.status(); diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/meeting/MeetingEnterProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/meeting/MeetingEnterProtocol.java index baa2f83..9602e44 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/meeting/MeetingEnterProtocol.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/meeting/MeetingEnterProtocol.java @@ -1,5 +1,31 @@ package com.acgist.taoyao.signal.protocol.meeting; -public class MeetingEnterProtocol { +import java.util.Map; + +import org.springframework.stereotype.Component; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.client.ClientSession; +import com.acgist.taoyao.signal.event.meeting.MeetingEnterEvent; +import com.acgist.taoyao.signal.protocol.ProtocolMapAdapter; + +/** + * 进入会议信令 + * + * @author acgist + */ +@Component +public class MeetingEnterProtocol extends ProtocolMapAdapter { + + public static final Integer PID = 4002; + + public MeetingEnterProtocol() { + super(PID, "进入会议信令"); + } + + @Override + public void execute(String sn, Map body, Message message, ClientSession session) { + this.publishEvent(new MeetingEnterEvent(body, message, session)); + } } diff --git a/taoyao-webrtc/README.md b/taoyao-webrtc/README.md index 1a25a90..f2c13ed 100644 --- a/taoyao-webrtc/README.md +++ b/taoyao-webrtc/README.md @@ -49,4 +49,3 @@ SDP只是一种信息格式的描述标准,不属于传输协议,但是可 |信令通道|自己实现| |会话通道|SIP/SDP| |媒体通道|RTP/RTCP/SRTP/SRTCP| -