From 7fe3babab01787209726debfdb2355eb1e3a1f4d Mon Sep 17 00:00:00 2001
From: acgist <289547414@qq.com>
Date: Mon, 28 Nov 2022 19:33:17 +0800
Subject: [PATCH] [*] mesh
---
docs/FFmpeg.md | 92 -----
pom.xml | 44 ---
.../boot/config/MediaAudioProperties.java | 16 +-
.../boot/config/MediaVideoProperties.java | 2 +-
taoyao-media/pom.xml | 14 -
.../com/acgist/taoyao/meeting/Meeting.java | 10 +
.../acgist/taoyao/meeting/MeetingManager.java | 3 +-
.../listener/MeetingEnterListener.java | 1 +
.../src/main/resources/application.yml | 6 +-
.../resources/static/javascript/taoyao.js | 314 ++++++++++--------
.../src/main/resources/static/meeting.html | 7 +-
taoyao-signal/README.md | 16 +-
.../signal/client/ClientSessionAdapter.java | 2 +-
.../signal/client/ClientSessionManager.java | 25 +-
.../signal/event/media/MediaAnswerEvent.java | 34 ++
.../event/media/MediaCandidateEvent.java | 7 +-
.../signal/event/media/MediaOfferEvent.java | 34 ++
.../signal/event/media/MediaPublishEvent.java | 2 +-
.../event/media/MediaSubscribeEvent.java | 2 +-
.../protocol/media/MediaAnswerProtocol.java | 30 ++
.../media/MediaCandidateProtocol.java | 2 +-
.../protocol/media/MediaOfferProtocol.java | 30 ++
.../mesh/config/MeshAutoConfiguration.java | 14 +
.../mesh/listener/MediaAnswerListener.java | 33 ++
.../mesh/listener/MediaCandidateListener.java | 13 +-
.../mesh/listener/MediaOfferListener.java | 33 ++
.../mesh/listener/MediaPublishListener.java | 7 +
.../mesh/listener/MediaSubscribeListener.java | 7 +
28 files changed, 474 insertions(+), 326 deletions(-)
delete mode 100644 docs/FFmpeg.md
create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaAnswerEvent.java
create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaOfferEvent.java
create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAnswerProtocol.java
create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaOfferProtocol.java
create mode 100644 taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaAnswerListener.java
create mode 100644 taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaOfferListener.java
diff --git a/docs/FFmpeg.md b/docs/FFmpeg.md
deleted file mode 100644
index 525add6..0000000
--- a/docs/FFmpeg.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# FFmpeg
-
-默认使用`ffmpeg-platform`所以不用安装,如果使用本地FFmpeg需要自己安装。
-
-## FFmpeg
-
-```
-# nasm
-wget https://www.nasm.us/pub/nasm/releasebuilds/2.14/nasm-2.14.tar.gz
-tar zxvf nasm-2.14.tar.gz
-cd nasm-2.14
-./configure --prefix=/usr/local/nasm
-make -j && make install
-# 环境变量
-vim /etc/profile
-export PATH=$PATH:/usr/local/nasm/bin
-source /etc/profile
-
-# yasm
-wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
-tar zxvf yasm-1.3.0.tar.gz
-cd yasm-1.3.0
-./configure --prefix=/usr/local/yasm
-make -j && make install
-# 环境变量
-vim /etc/profile
-export PATH=$PATH:/usr/local/yasm/bin
-source /etc/profile
-
-# x264
-git clone https://code.videolan.org/videolan/x264.git
-cd x264
-./configure --prefix=/usr/local/x264 --libdir=/usr/local/lib --includedir=/usr/local/include --enable-shared --enable-static
-make -j && make install
-# 环境变量
-vim /etc/profile
-export PATH=$PATH:/usr/local/x264/bin
-source /etc/profile
-
-# 编码解码
-# acc
-https://github.com/mstorsjo/fdk-aac.git
---enable-libfdk_aac
-# vpx
-https://github.com/webmproject/libvpx.git
---enable-libvpx
-# x265
-https://bitbucket.org/multicoreware/x265
---enable-libx265
-# opus
-https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz
---enable-libopus
-
-# ffmpeg
-wget http://www.ffmpeg.org/releases/ffmpeg-4.3.1.tar.xz
-tar xvJf ffmpeg-4.3.1.tar.xz
-cd ffmpeg-4.3.1
-./configure --prefix=/usr/local/ffmpeg --enable-gpl --enable-shared --enable-libx264
-# --enable-cuda --enable-cuvid --enable-nvenc --nvcc=/usr/local/cuda-11.0/bin/nvcc
-make -j && make install
-# 环境变量
-vim /etc/profile
-export PATH=$PATH:/usr/local/ffmpeg/bin
-source /etc/profile
-
-# lib
-vim /etc/ld.so.conf
-/usr/local/x264/lib/
-/usr/local/ffmpeg/lib/
-ldconfig
-
-# 查看版本
-ffmpeg -version
-# 查看编解码
-ffmpeg -codecs
-# 格式化文件
-ffmpeg -y -i source.mkv -c copy target.mp4
-# 查看文件格式
-ffprobe -v error -show_streams -print_format json source.mp4
-```
-
-## GPU
-
-```
-驱动
-# cuda
-https://developer.nvidia.com/cuda-downloads
-# nv-codec-headers
-https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
-# 验证
-nvidia-smi
-```
diff --git a/pom.xml b/pom.xml
index ddbc793..723b035 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,6 @@
17
1.5.8
- 5.1.2
1.18.24
6.18.0
2.0.0-RC1
@@ -189,49 +188,6 @@
kurento-client
${kurento.version}
-
-
- org.bytedeco
- ffmpeg-platform
- ${ffmpeg.version}-${javacv.version}
-
-
- org.bytedeco
- ffmpeg
-
-
- org.bytedeco
- javacpp
-
-
-
-
- org.bytedeco
- ffmpeg
- ${ffmpeg.version}-${javacv.version}
- ${javacv.os.version}
-
-
- org.bytedeco
- javacpp
- ${javacv.version}
- ${javacv.os.version}
-
org.apache.commons
diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaAudioProperties.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaAudioProperties.java
index 646a747..ffb7099 100644
--- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaAudioProperties.java
+++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaAudioProperties.java
@@ -21,10 +21,6 @@ public class MediaAudioProperties {
*/
public enum Format {
- /**
- * ACC
- */
- ACC,
/**
* PCM
*/
@@ -41,16 +37,16 @@ public class MediaAudioProperties {
*/
@Schema(title = "格式", description = "格式")
private Format format;
+ /**
+ * 采样数
+ */
+ @Schema(title = "采样数", description = "采样数", example = "16")
+ private Integer sampleSize;
/**
* 采样率
* 8000|16000|32000|48000
*/
@Schema(title = "采样率", description = "采样率", example = "8000|16000|32000|48000")
- private Integer samplerate;
- /**
- * 采样数
- */
- @Schema(title = "采样数", description = "采样数", example = "16")
- private Integer samplesize;
+ private Integer sampleRate;
}
diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaVideoProperties.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaVideoProperties.java
index 7f9c6ac..b010440 100644
--- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaVideoProperties.java
+++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/MediaVideoProperties.java
@@ -54,7 +54,7 @@ public class MediaVideoProperties {
* 帧率(流畅)
*/
@Schema(title = "帧率", description = "帧率影响流程", example = "20|24|30|60")
- private Integer framerate;
+ private Integer frameRate;
/**
* 分辨率(画面大小)
*/
diff --git a/taoyao-media/pom.xml b/taoyao-media/pom.xml
index b077856..0445f84 100644
--- a/taoyao-media/pom.xml
+++ b/taoyao-media/pom.xml
@@ -37,20 +37,6 @@
com.acgist
taoyao-webrtc-kurento
-
- org.bytedeco
- ffmpeg-platform
-
-
- org.bytedeco
- ffmpeg
- ${javacv.os.version}
-
-
- org.bytedeco
- javacpp
- ${javacv.os.version}
-
\ No newline at end of file
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 3dde72a..5a397d9 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
@@ -21,6 +21,16 @@ public class Meeting {
*/
@Schema(title = "会议标识", description = "会议标识")
private String id;
+ /**
+ * 会议名称
+ */
+ @Schema(title = "会议名称", description = "会议名称")
+ private String name;
+ /**
+ * 会议密码
+ */
+ @Schema(title = "会议密码", description = "会议密码")
+ private String password;
/**
* 终端会话标识列表
*/
diff --git a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/MeetingManager.java b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/MeetingManager.java
index 233c5e3..4e4acee 100644
--- a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/MeetingManager.java
+++ b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/MeetingManager.java
@@ -65,7 +65,8 @@ public class MeetingManager {
*/
public Meeting create(String sn) {
final Meeting meeting = new Meeting();
- meeting.setId(this.idService.buildIdToString());
+// meeting.setId(this.idService.buildIdToString());
+ meeting.setId("1");
meeting.setSns(new CopyOnWriteArrayList<>());
meeting.setCreator(sn);
meeting.addSn(sn);
diff --git a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingEnterListener.java b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingEnterListener.java
index 03946b2..18266b1 100644
--- a/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingEnterListener.java
+++ b/taoyao-meeting/src/main/java/com/acgist/taoyao/meeting/listener/MeetingEnterListener.java
@@ -32,6 +32,7 @@ public class MeetingEnterListener extends MeetingListenerAdapter !sn.equals(v))
.forEach(v -> this.clientSessionManager.unicast(v, message));
diff --git a/taoyao-server/src/main/resources/application.yml b/taoyao-server/src/main/resources/application.yml
index 21fbb85..46f146c 100644
--- a/taoyao-server/src/main/resources/application.yml
+++ b/taoyao-server/src/main/resources/application.yml
@@ -59,12 +59,12 @@ taoyao:
media:
audio:
format: OPUS
- samplesize: 16
- samplerate: 32000
+ sample-size: 16
+ sample-rate: 32000
video:
format: H264
bitrate: 1200
- framerate: 24
+ frame-rate: 24
resolution: 1280*760
quality: high|standard|quick
# WebRTC配置
diff --git a/taoyao-server/src/main/resources/static/javascript/taoyao.js b/taoyao-server/src/main/resources/static/javascript/taoyao.js
index f5a22a4..44cef8d 100644
--- a/taoyao-server/src/main/resources/static/javascript/taoyao.js
+++ b/taoyao-server/src/main/resources/static/javascript/taoyao.js
@@ -1,20 +1,25 @@
-/** 桃夭WebRTC终端核心功能 */
+/**
+ * 桃夭WebRTC终端核心功能
+ *
+ * 代码注意:
+ * 1. undefined判断使用两个等号,其他情况使用三个。
+*/
/** 兼容 */
const RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate;
const RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
const RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
/** 默认音频配置 */
const defaultAudioConfig = {
+ // 设备
+ // deviceId : '',
// 音量:0~1
volume: 0.5,
// 延迟大小(单位毫秒):500毫秒以内较好
latency: 0.4,
- // 设备
- // deviceId : '',
- // 采样率:8000|16000|32000|48000
- sampleRate: 48000,
// 采样数:16
sampleSize: 16,
+ // 采样率:8000|16000|32000|48000
+ sampleRate: 32000,
// 声道数量:1|2
channelCount : 1,
// 是否开启自动增益:true|false
@@ -28,16 +33,14 @@ const defaultAudioConfig = {
};
/** 默认视频配置 */
const defaultVideoConfig = {
+ // 设备
+ // deviceId: '',
// 宽度
width: 1280,
// 高度
height: 720,
- // 设备
- // deviceId: '',
// 帧率
frameRate: 24,
- // 裁切
- // resizeMode: '',
// 选摄像头:user|left|right|environment
facingMode: 'environment'
}
@@ -57,7 +60,7 @@ const defaultRPCConfig = {
/** 信令配置 */
const signalConfig = {
/** 当前终端SN */
- sn: localStorage.getItem('taoyao.sn', 'taoyao'),
+ sn: localStorage.getItem('taoyao.sn') || 'taoyao',
/** 当前版本 */
version: '1.0.0',
// 信令授权
@@ -76,7 +79,11 @@ const signalProtocol = {
/** 订阅 */
subscribe: 5002,
/** 候选 */
- candidate: 5004
+ offer: 5997,
+ /** Answer */
+ answer: 5998,
+ /** 候选 */
+ candidate: 5999
},
/** 终端信令 */
client: {
@@ -303,6 +310,12 @@ const signalChannel = {
case signalProtocol.media.subscribe:
this.defaultMediaSubscribe(data);
break;
+ case signalProtocol.media.offer:
+ this.defaultMediaOffer(data);
+ break;
+ case signalProtocol.media.answer:
+ this.defaultMediaAnswer(data);
+ break;
case signalProtocol.media.candidate:
this.defaultMediaCandidate(data);
break;
@@ -329,7 +342,7 @@ const signalChannel = {
/** 终端默认回调 */
defaultClientConfig: function(data) {
this.taoyao
- .configMedia(data.body.media)
+ .configMedia(data.body.media.audio, data.body.media.video)
.configWebrtc(data.body.webrtc);
},
defaultClientReboot: function(data) {
@@ -338,52 +351,71 @@ const signalChannel = {
},
/** 默认媒体回调 */
defaultMediaPublish: function(data) {
- this.taoyao.localMediaChannel.setRemoteDescription(new RTCSessionDescription(data.body));
},
defaultMediaSubscribe: function(data) {
let self = this;
const from = data.body.from;
- let remote = this.taoyao.remoteClientFilter(from);
- if(!remote) {
- remote = new TaoyaoClient(from);
- this.taoyao.remoteClient.push(remote);
- }
- self.taoyao.remoteMediaChannel.setRemoteDescription(new RTCSessionDescription(data.body));
- self.taoyao.remoteMediaChannel.createAnswer().then(description => {
- console.debug('Local Create Answer', description);
- self.taoyao.remoteMediaChannel.setLocalDescription(description);
+ this.taoyao.remoteClientFilter(from, true);
+ self.taoyao.localMediaChannel.createOffer().then(description => {
+ console.debug('Local Create Offer', description);
+ self.taoyao.localMediaChannel.setLocalDescription(description);
self.push(signalProtocol.buildProtocol(
- signalProtocol.media.publish,
+ signalProtocol.media.offer,
{
to: from,
- sdp: description.sdp,
- type: description.type
+ sdp: {
+ sdp: description.sdp,
+ type: description.type
+ }
}
));
});
},
+ defaultMediaOffer: function(data) {
+ let self = this;
+ const from = data.body.from;
+ this.taoyao.remoteClientFilter(from, true);
+ self.taoyao.remoteMediaChannel.setRemoteDescription(new RTCSessionDescription(data.body.sdp));
+ self.taoyao.remoteMediaChannel.createAnswer().then(description => {
+ console.debug('Remote Create Answer', description);
+ self.taoyao.remoteMediaChannel.setLocalDescription(description);
+ self.push(signalProtocol.buildProtocol(
+ signalProtocol.media.answer,
+ {
+ to: from,
+ sdp: {
+ sdp: description.sdp,
+ type: description.type
+ }
+ }
+ ));
+ });
+ },
+ defaultMediaAnswer: function(data) {
+ this.taoyao.localMediaChannel.setRemoteDescription(new RTCSessionDescription(data.body.sdp));
+ },
defaultMediaCandidate: function(data) {
- if(
- !data.body.candidate ||
- !data.body.candidate.candidate ||
- !data.body.candidate.sdpMid ||
- !data.body.candidate.sdpMLineIndex
- ) {
- console.warn('候选缺失要素', data);
+ if(!this.taoyao.checkCandidate(data.body.candidate)) {
+ console.debug('候选缺失要素', data);
return;
}
- let candidate = new RTCIceCandidate(data.body.candidate);
- this.taoyao.remoteMediaChannel.addIceCandidate(candidate);
+ console.debug('Set ICE Candidate', this.taoyao.remoteMediaChannel);
+ if(data.body.type === 'local') {
+ this.taoyao.remoteMediaChannel.addIceCandidate(new RTCIceCandidate(data.body.candidate));
+ } else {
+ this.taoyao.localMediaChannel.addIceCandidate(new RTCIceCandidate(data.body.candidate));
+ }
},
/** 会议默认回调 */
defaultMeetingEnter: function(data) {
- this.taoyao
- .mediaSubscribe(data.body.sn);
+ this.taoyao.mediaSubscribe(data.body.sn);
}
};
/** 终端 */
function TaoyaoClient(
- sn
+ sn,
+ audioEnabled,
+ videoEnabled
) {
/** 终端标识 */
this.sn = sn;
@@ -391,12 +423,15 @@ function TaoyaoClient(
this.video = null;
/** 媒体信息 */
this.stream = null;
- this.audioTrack = null;
- this.videoTrack = null;
- /** 媒体状态 */
+ this.audioTrack = [];
+ this.videoTrack = [];
+ /** 媒体状态:是否含有 */
this.audioStatus = false;
this.videoStatus = false;
this.recordStatus = false;
+ /** 媒体状态:是否播放 */
+ this.audioEnabled = audioEnabled == undefined ? true : audioEnabled;
+ this.videoEnabled = videoEnabled == undefined ? true : videoEnabled;
/** 重置 */
this.reset = function() {
}
@@ -406,80 +441,80 @@ function TaoyaoClient(
return this;
};
/** 重新加载 */
- this.load = function() {
- this.video.load();
+ this.load = async function() {
+ await this.video.load();
return this;
}
/** 暂停视频 */
- this.pause = function() {
- this.video.pause();
+ this.pause = async function() {
+ await this.video.pause();
return this;
};
/** 关闭视频 */
- this.close = function() {
- this.video.close();
+ this.close = async function() {
+ await this.video.close();
return this;
};
- /** 设置视频对象 */
- this.buildVideo = async function(videoId, stream, track) {
- if(!this.video) {
+ /** 设置媒体 */
+ this.buildStream = async function(videoId, stream, track) {
+ if(!this.video && videoId) {
this.video = document.getElementById(videoId);
}
- await this.buildStream(stream, track);
- return this;
- };
- /** 设置媒体流 */
- this.buildStream = async function(stream, track) {
- if(stream) {
- if(track) {
- if(!this.stream) {
- this.stream = stream;
- this.video.srcObject = this.stream;
- }
- // TODO:删除旧的
- this.stream.addTrack(track);
- if(track.kind === 'audio') {
- this.audioTrack = track;
- this.audioStatus = true;
- } else if(track.kind === 'video') {
- this.videoTrack = track;
- this.videoStatus = true;
- }
- await this.video.load();
- } else {
- this.stream = stream;
- this.video.srcObject = stream;
- let audioTrack = stream.getAudioTracks();
- let videoTrack = stream.getVideoTracks();
- if(audioTrack && audioTrack.length) {
- this.audioTrack = audioTrack;
- this.audioStatus = true;
- }
- if(videoTrack && videoTrack.length) {
- this.videoTrack = videoTrack;
- this.videoStatus = true;
- }
- await this.video.load();
- }
- console.debug('设置媒体流', this.video, this.stream, this.audioTrack, this.videoTrack);
- await this.play();
+ if(!this.video) {
+ throw new Error('视频对象无效:' + videoId);
}
+ if(!this.stream) {
+ this.stream = new MediaStream();
+ this.video.srcObject = this.stream;
+ }
+ if(track) {
+ if(track.kind === 'audio') {
+ this.buildAudioTrack(track);
+ }
+ if(track.kind === 'video') {
+ this.buildVideoTrack(track);
+ }
+ } else {
+ let audioTrack = stream.getAudioTracks();
+ let videoTrack = stream.getVideoTracks();
+ if(audioTrack && audioTrack.length) {
+ audioTrack.forEach(v => this.buildAudioTrack(v));
+ }
+ if(videoTrack && videoTrack.length) {
+ videoTrack.forEach(v => this.buildVideoTrack(v));
+ }
+ }
+ console.debug('设置媒体', this.video, this.stream, this.audioTrack, this.videoTrack);
+ await this.load();
+ await this.play();
return this;
};
/** 设置音频流 */
- this.buildAudioTrack = function() {
+ this.buildAudioTrack = function(track) {
// 关闭旧的
// 创建新的
+ this.audioStatus = true;
+ this.audioTrack.push(track);
+ if(this.audioEnabled) {
+ this.stream.addTrack(track);
+ }
};
/** 设置视频流 */
- this.buildVideoTrack = function() {
+ this.buildVideoTrack = function(track) {
// 关闭旧的
// 创建新的
+ this.videoStatus = true;
+ this.videoTrack.push(track);
+ if(this.videoEnabled) {
+ this.stream.addTrack(track);
+ }
};
}
/** 桃夭 */
function Taoyao(
- webSocket
+ webSocket,
+ localClientAudioEnabled,
+ localClientVideoEnabled
) {
/** WebRTC配置 */
this.webrtc = null;
@@ -497,6 +532,9 @@ function Taoyao(
/** 本地终端 */
this.localClient = null;
this.localMediaChannel = null;
+ /** 本地媒体状态 */
+ this.localClientAudioEnabled = localClientAudioEnabled == undefined ? false : localClientAudioEnabled;
+ this.localClientVideoEnabled = localClientVideoEnabled == undefined ? true : localClientVideoEnabled;
/** 远程终端 */
this.remoteClient = [];
this.remoteMediaChannel = null;
@@ -505,7 +543,7 @@ function Taoyao(
/** 媒体配置 */
this.configMedia = function(audio = {}, video = {}) {
this.audioConfig = {...this.audioConfig, ...audio};
- this.videoCofnig = {...this.videoCofnig, ...video};
+ this.videoConfig = {...this.videoConfig, ...video};
console.debug('终端媒体配置', this.audioConfig, this.videoConfig);
return this;
};
@@ -586,13 +624,17 @@ function Taoyao(
});
};
/** 远程终端过滤 */
- this.remoteClientFilter = function(sn) {
+ this.remoteClientFilter = function(sn, autoBuild) {
let array = this.remoteClient.filter(v => v.sn === sn);
- if(array.length <= 0) {
- return null;
+ let remote = null;
+ if(array.length > 0) {
+ remote = array[0];
+ } else if(autoBuild) {
+ remote = new TaoyaoClient(sn);
+ this.remoteClient.push(remote);
}
- return this.remoteClient.filter(v => v.sn === sn)[0];
- }
+ return remote;
+ };
/** 关闭:关闭媒体 */
this.close = function() {
// TODO:释放资源
@@ -605,16 +647,12 @@ function Taoyao(
this.buildMediaChannel = async function(localVideoId, stream) {
let self = this;
// 本地视频
- this.localClient = new TaoyaoClient(signalConfig.sn);
- await this.localClient.buildVideo(localVideoId, stream);
+ this.localClient = new TaoyaoClient(signalConfig.sn, this.localClientAudioEnabled, this.localClientVideoEnabled);
+ await this.localClient.buildStream(localVideoId, stream);
// 本地通道
this.localMediaChannel = new RTCPeerConnection(defaultRPCConfig);
- if(this.localClient.audioTrack) {
- this.localClient.audioTrack.forEach(v => this.localMediaChannel.addTrack(v, this.localClient.stream));
- }
- if(this.localClient.videoTrack) {
- this.localClient.videoTrack.forEach(v => this.localMediaChannel.addTrack(v, this.localClient.stream));
- }
+ this.localClient.audioTrack.forEach(v => this.localMediaChannel.addTrack(v, this.localClient.stream));
+ this.localClient.videoTrack.forEach(v => this.localMediaChannel.addTrack(v, this.localClient.stream));
this.localMediaChannel.ontrack = function(e) {
console.debug('Local Media Track', e);
};
@@ -633,12 +671,18 @@ function Taoyao(
}
};
this.localMediaChannel.onicecandidate = function(e) {
- let sns = self.remoteClient.filter(v => v.video === null).map(v => v.sn);
- console.debug('Local ICE Candidate', sns, e);
+ // TODO:判断给谁
+ let to = self.remoteClient.map(v => v.sn)[0];
+ if(!self.checkCandidate(e.candidate)) {
+ console.debug('Send Local ICE Candidate Fail', e);
+ return;
+ }
+ console.debug('Send Local ICE Candidate', to, e);
self.push(signalProtocol.buildProtocol(
signalProtocol.media.candidate,
{
- sns: sns,
+ to: to,
+ type: 'local',
candidate: e.candidate
}
));
@@ -649,7 +693,7 @@ function Taoyao(
console.debug('Remote Media Track', e);
// TODO:匹配
let remote = self.remoteClient[0];
- remote.buildVideo(remote.sn, e.streams[0], e.track);
+ remote.buildStream(remote.sn, e.streams[0], e.track);
};
this.remoteMediaChannel.ondatachannel = function(channel) {
channel.onopen = function() {
@@ -666,12 +710,18 @@ function Taoyao(
}
};
this.remoteMediaChannel.onicecandidate = function(e) {
- let sns = self.remoteClient.filter(v => v.video === null).map(v => v.sn);
- console.debug('Remote ICE Candidate', sns, e);
+ // TODO:判断给谁
+ let to = self.remoteClient.map(v => v.sn)[0];
+ if(!self.checkCandidate(e.candidate)) {
+ console.debug('Send Remote ICE Candidate Fail', e);
+ return;
+ }
+ console.debug('Send Remote ICE Candidate', to, e);
self.push(signalProtocol.buildProtocol(
signalProtocol.media.candidate,
{
- sns: sns,
+ to: to,
+ type: 'remote',
candidate: e.candidate
}
));
@@ -679,30 +729,30 @@ function Taoyao(
console.debug('打开媒体通道', this.localMediaChannel, this.remoteMediaChannel);
return this;
};
+ /** 校验candidate */
+ this.checkCandidate = function(candidate) {
+ if(
+ !candidate ||
+ !candidate.candidate ||
+ candidate.sdpMid === null ||
+ candidate.sdpMid === null ||
+ candidate.sdpMLineIndex === undefined ||
+ candidate.sdpMLineIndex === undefined
+ ) {
+ return false;
+ }
+ return true;
+ };
/** 媒体信令 */
this.mediaSubscribe = function(sn, callback) {
let self = this;
- let remote = self.remoteClientFilter(sn);
- if(remote) {
- remote.reset();
- } else {
- remote = new TaoyaoClient(sn);
- this.remoteClient.push(remote);
- }
- if(self.webrtc.model === 'MESH') {
- self.localMediaChannel.createOffer().then(description => {
- console.debug('Local Create Offer', description);
- self.localMediaChannel.setLocalDescription(description);
- self.push(signalProtocol.buildProtocol(
- signalProtocol.media.subscribe,
- {
- to: sn,
- sdp: description.sdp,
- type: description.type
- }
- ), callback);
- });
- }
+ self.remoteClientFilter(sn, true);
+ self.push(signalProtocol.buildProtocol(
+ signalProtocol.media.subscribe,
+ {
+ to: sn
+ }
+ ), callback);
};
/** 会议信令 */
this.meetingCreate = function(callback) {
diff --git a/taoyao-server/src/main/resources/static/meeting.html b/taoyao-server/src/main/resources/static/meeting.html
index 90eb4e9..b42237f 100644
--- a/taoyao-server/src/main/resources/static/meeting.html
+++ b/taoyao-server/src/main/resources/static/meeting.html
@@ -73,10 +73,11 @@
methods: {
// 信令回调:返回true表示已经处理
callback: function(data) {
+ let self = this;
switch(data.header.pid) {
- case signalProtocol.client.heartbeat:
- // 心跳
- return true;
+ case signalProtocol.client.config:
+ // 如果需要下发配置生效需要在此打开媒体通道
+ return false;
}
return false;
},
diff --git a/taoyao-signal/README.md b/taoyao-signal/README.md
index 903061c..b262493 100644
--- a/taoyao-signal/README.md
+++ b/taoyao-signal/README.md
@@ -373,10 +373,6 @@
取消订阅终端媒体流(终端取消拉流)
-### 候选信令(5004)
-
-IceCandidate
-
### 暂停信令(5004)
终端->服务端
@@ -397,6 +393,18 @@ MCU/SFU模式有效
配置订阅媒体:码率、帧率、分辨率等等
+### Offer信令(5997)
+
+Offer
+
+### Answer信令(5998)
+
+Answer
+
+### 候选信令(5999)
+
+IceCandidate
+
## 测试
```
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionAdapter.java
index d2a4c27..a851ff4 100644
--- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionAdapter.java
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionAdapter.java
@@ -49,7 +49,7 @@ public abstract class ClientSessionAdapter implements C
@Override
public boolean timeout(long timeout) {
- return !this.authorized && System.currentTimeMillis() - this.time > timeout;
+ return System.currentTimeMillis() - this.time > timeout;
}
@Override
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionManager.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionManager.java
index 7818349..c14c15f 100644
--- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionManager.java
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/client/ClientSessionManager.java
@@ -36,7 +36,7 @@ public class ClientSessionManager {
@Scheduled(cron = "${taoyao.scheduled.session:0 * * * * ?}")
public void scheduled() {
- this.closeTimeoutSession();
+ this.closeTimeout();
}
/**
@@ -65,7 +65,9 @@ public class ClientSessionManager {
* @param message 消息
*/
public void unicast(String to, Message message) {
- this.sessions.stream().filter(v -> v.filterSn(to)).forEach(v -> {
+ this.sessions().stream()
+ .filter(v -> v.filterSn(to))
+ .forEach(v -> {
message.getHeader().setSn(v.sn());
v.push(message);
});
@@ -77,7 +79,7 @@ public class ClientSessionManager {
* @param message 消息
*/
public void broadcast(Message message) {
- this.sessions.forEach(v -> {
+ this.sessions().forEach(v -> {
message.getHeader().setSn(v.sn());
v.push(message);
});
@@ -90,7 +92,9 @@ public class ClientSessionManager {
* @param message 消息
*/
public void broadcast(String from, Message message) {
- this.sessions.stream().filter(v -> v.filterNoneSn(from)).forEach(v -> {
+ this.sessions().stream().
+ filter(v -> v.filterNoneSn(from))
+ .forEach(v -> {
message.getHeader().setSn(v.sn());
v.push(message);
});
@@ -102,7 +106,7 @@ public class ClientSessionManager {
* @return 终端会话
*/
public ClientSession session(String sn) {
- return this.sessions.stream()
+ return this.sessions().stream()
.filter(v -> StringUtils.equals(sn, v.sn()))
.findFirst()
.orElse(null);
@@ -122,14 +126,18 @@ public class ClientSessionManager {
* @return 所有终端会话
*/
public List sessions() {
- return this.sessions;
+ return this.sessions.stream()
+ .filter(ClientSession::authorized)
+ .toList();
}
/**
* @return 所有终端状态
*/
public List status() {
- return this.sessions().stream().map(ClientSession::status).toList();
+ return this.sessions().stream()
+ .map(ClientSession::status)
+ .toList();
}
/**
@@ -160,9 +168,10 @@ public class ClientSessionManager {
/**
* 定时关闭超时会话
*/
- private void closeTimeoutSession() {
+ private void closeTimeout() {
log.debug("定时关闭超时会话");
this.sessions.stream()
+ .filter(v -> !v.authorized())
.filter(v -> v.timeout(this.taoyaoProperties.getTimeout()))
.forEach(v -> {
log.debug("关闭超时会话:{}", v);
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaAnswerEvent.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaAnswerEvent.java
new file mode 100644
index 0000000..96429d9
--- /dev/null
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaAnswerEvent.java
@@ -0,0 +1,34 @@
+package com.acgist.taoyao.signal.event.media;
+
+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;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Answer事件
+ *
+ * @author acgist
+ */
+@Getter
+@Setter
+public class MediaAnswerEvent extends ApplicationEventAdapter {
+
+ private static final long serialVersionUID = 1L;
+
+ public MediaAnswerEvent(String sn, Map, ?> body, Message message, ClientSession session) {
+ super(sn, body, message, session);
+ }
+
+ /**
+ * @return 接收终端标识
+ */
+ public String getTo() {
+ return this.get("to");
+ }
+
+}
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaCandidateEvent.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaCandidateEvent.java
index f1fa5b7..6d3a1b3 100644
--- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaCandidateEvent.java
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaCandidateEvent.java
@@ -1,6 +1,5 @@
package com.acgist.taoyao.signal.event.media;
-import java.util.List;
import java.util.Map;
import com.acgist.taoyao.boot.model.Message;
@@ -26,10 +25,10 @@ public class MediaCandidateEvent extends ApplicationEventAdapter {
}
/**
- * @return 终端列表
+ * @return 接收终端标识
*/
- public List getSns() {
- return this.get("sns");
+ public String getTo() {
+ return this.get("to");
}
}
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaOfferEvent.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaOfferEvent.java
new file mode 100644
index 0000000..e746320
--- /dev/null
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaOfferEvent.java
@@ -0,0 +1,34 @@
+package com.acgist.taoyao.signal.event.media;
+
+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;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Offer事件
+ *
+ * @author acgist
+ */
+@Getter
+@Setter
+public class MediaOfferEvent extends ApplicationEventAdapter {
+
+ private static final long serialVersionUID = 1L;
+
+ public MediaOfferEvent(String sn, Map, ?> body, Message message, ClientSession session) {
+ super(sn, body, message, session);
+ }
+
+ /**
+ * @return 接收终端标识
+ */
+ public String getTo() {
+ return this.get("to");
+ }
+
+}
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaPublishEvent.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaPublishEvent.java
index 75c3189..415cc3d 100644
--- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaPublishEvent.java
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaPublishEvent.java
@@ -25,7 +25,7 @@ public class MediaPublishEvent extends ApplicationEventAdapter {
}
/**
- * @return 终端标识(发布给谁)
+ * @return 接收终端标识
*/
public String getTo() {
return this.get("to");
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaSubscribeEvent.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaSubscribeEvent.java
index 75afee1..24e832b 100644
--- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaSubscribeEvent.java
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/media/MediaSubscribeEvent.java
@@ -25,7 +25,7 @@ public class MediaSubscribeEvent extends ApplicationEventAdapter {
}
/**
- * @return 终端标识(订阅的谁)
+ * @return 接收终端标识
*/
public String getTo() {
return this.get("to");
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAnswerProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAnswerProtocol.java
new file mode 100644
index 0000000..a07d997
--- /dev/null
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaAnswerProtocol.java
@@ -0,0 +1,30 @@
+package com.acgist.taoyao.signal.protocol.media;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.client.ClientSession;
+import com.acgist.taoyao.signal.event.media.MediaAnswerEvent;
+import com.acgist.taoyao.signal.protocol.ProtocolMapAdapter;
+
+/**
+ * Answer信令
+ *
+ * @author acgist
+ */
+@Protocol
+public class MediaAnswerProtocol extends ProtocolMapAdapter {
+
+ public static final Integer PID = 5998;
+
+ public MediaAnswerProtocol() {
+ super(PID, "Answer信令");
+ }
+
+ @Override
+ public void execute(String sn, Map, ?> body, Message message, ClientSession session) {
+ this.publishEvent(new MediaAnswerEvent(sn, body, message, session));
+ }
+
+}
\ No newline at end of file
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaCandidateProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaCandidateProtocol.java
index d06a1e8..fb00a02 100644
--- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaCandidateProtocol.java
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaCandidateProtocol.java
@@ -16,7 +16,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolMapAdapter;
@Protocol
public class MediaCandidateProtocol extends ProtocolMapAdapter {
- public static final Integer PID = 5004;
+ public static final Integer PID = 5999;
public MediaCandidateProtocol() {
super(PID, "候选信令");
diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaOfferProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaOfferProtocol.java
new file mode 100644
index 0000000..c5c7f53
--- /dev/null
+++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/media/MediaOfferProtocol.java
@@ -0,0 +1,30 @@
+package com.acgist.taoyao.signal.protocol.media;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.annotation.Protocol;
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.client.ClientSession;
+import com.acgist.taoyao.signal.event.media.MediaOfferEvent;
+import com.acgist.taoyao.signal.protocol.ProtocolMapAdapter;
+
+/**
+ * Offer信令
+ *
+ * @author acgist
+ */
+@Protocol
+public class MediaOfferProtocol extends ProtocolMapAdapter {
+
+ public static final Integer PID = 5997;
+
+ public MediaOfferProtocol() {
+ super(PID, "Offer信令");
+ }
+
+ @Override
+ public void execute(String sn, Map, ?> body, Message message, ClientSession session) {
+ this.publishEvent(new MediaOfferEvent(sn, body, message, session));
+ }
+
+}
\ No newline at end of file
diff --git a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/config/MeshAutoConfiguration.java b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/config/MeshAutoConfiguration.java
index 8281c6e..0d5ca0f 100644
--- a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/config/MeshAutoConfiguration.java
+++ b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/config/MeshAutoConfiguration.java
@@ -5,7 +5,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import com.acgist.taoyao.webrtc.mesh.listener.MediaAnswerListener;
import com.acgist.taoyao.webrtc.mesh.listener.MediaCandidateListener;
+import com.acgist.taoyao.webrtc.mesh.listener.MediaOfferListener;
import com.acgist.taoyao.webrtc.mesh.listener.MediaPublishListener;
import com.acgist.taoyao.webrtc.mesh.listener.MediaSubscribeListener;
@@ -30,6 +32,18 @@ public class MeshAutoConfiguration {
return new MediaSubscribeListener();
}
+ @Bean
+ @ConditionalOnMissingBean
+ public MediaOfferListener mediaOfferListener() {
+ return new MediaOfferListener();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public MediaAnswerListener mediaAnswerListener() {
+ return new MediaAnswerListener();
+ }
+
@Bean
@ConditionalOnMissingBean
public MediaCandidateListener mediaCandidateListener() {
diff --git a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaAnswerListener.java b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaAnswerListener.java
new file mode 100644
index 0000000..7c55013
--- /dev/null
+++ b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaAnswerListener.java
@@ -0,0 +1,33 @@
+package com.acgist.taoyao.webrtc.mesh.listener;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.event.media.MediaAnswerEvent;
+import com.acgist.taoyao.signal.listener.MediaListenerAdapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Answer监听
+ *
+ * @author acgist
+ */
+@Slf4j
+public class MediaAnswerListener extends MediaListenerAdapter {
+
+ @Override
+ public void onApplicationEvent(MediaAnswerEvent event) {
+ final String sn = event.getSn();
+ final String to = event.getTo();
+ if(sn.equals(to)) {
+ log.debug("忽略Answer消息(相同终端):{}-{}", sn, to);
+ return;
+ }
+ final Message message = event.getMessage();
+ final Map mergeBody = event.mergeBody();
+ mergeBody.put("from", sn);
+ this.clientSessionManager.unicast(to, message);
+ }
+
+}
diff --git a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaCandidateListener.java b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaCandidateListener.java
index 35dc916..19368f3 100644
--- a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaCandidateListener.java
+++ b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaCandidateListener.java
@@ -1,32 +1,33 @@
package com.acgist.taoyao.webrtc.mesh.listener;
-import java.util.List;
import java.util.Map;
-import org.apache.commons.collections4.CollectionUtils;
-
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.event.media.MediaCandidateEvent;
import com.acgist.taoyao.signal.listener.MediaListenerAdapter;
+import lombok.extern.slf4j.Slf4j;
+
/**
* 候选监听
*
* @author acgist
*/
+@Slf4j
public class MediaCandidateListener extends MediaListenerAdapter {
@Override
public void onApplicationEvent(MediaCandidateEvent event) {
final String sn = event.getSn();
- final List sns = event.getSns();
- if(CollectionUtils.isEmpty(sns)) {
+ final String to = event.getTo();
+ if(sn.equals(to)) {
+ log.debug("忽略候选消息(相同终端):{}-{}", sn, to);
return;
}
final Message message = event.getMessage();
final Map mergeBody = event.mergeBody();
mergeBody.put("from", sn);
- sns.forEach(to -> this.clientSessionManager.unicast(to, message));
+ this.clientSessionManager.unicast(to, message);
}
}
diff --git a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaOfferListener.java b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaOfferListener.java
new file mode 100644
index 0000000..2ab7053
--- /dev/null
+++ b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaOfferListener.java
@@ -0,0 +1,33 @@
+package com.acgist.taoyao.webrtc.mesh.listener;
+
+import java.util.Map;
+
+import com.acgist.taoyao.boot.model.Message;
+import com.acgist.taoyao.signal.event.media.MediaOfferEvent;
+import com.acgist.taoyao.signal.listener.MediaListenerAdapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Offer监听
+ *
+ * @author acgist
+ */
+@Slf4j
+public class MediaOfferListener extends MediaListenerAdapter {
+
+ @Override
+ public void onApplicationEvent(MediaOfferEvent event) {
+ final String sn = event.getSn();
+ final String to = event.getTo();
+ if(sn.equals(to)) {
+ log.debug("忽略Offer消息(相同终端):{}-{}", sn, to);
+ return;
+ }
+ final Message message = event.getMessage();
+ final Map mergeBody = event.mergeBody();
+ mergeBody.put("from", sn);
+ this.clientSessionManager.unicast(to, message);
+ }
+
+}
diff --git a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaPublishListener.java b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaPublishListener.java
index 2033b55..7363389 100644
--- a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaPublishListener.java
+++ b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaPublishListener.java
@@ -6,17 +6,24 @@ import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.event.media.MediaPublishEvent;
import com.acgist.taoyao.signal.listener.MediaListenerAdapter;
+import lombok.extern.slf4j.Slf4j;
+
/**
* 发布监听
*
* @author acgist
*/
+@Slf4j
public class MediaPublishListener extends MediaListenerAdapter {
@Override
public void onApplicationEvent(MediaPublishEvent event) {
final String sn = event.getSn();
final String to = event.getTo();
+ if(sn.equals(to)) {
+ log.debug("忽略发布消息(相同终端):{}-{}", sn, to);
+ return;
+ }
final Message message = event.getMessage();
final Map mergeBody = event.mergeBody();
mergeBody.put("from", sn);
diff --git a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaSubscribeListener.java b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaSubscribeListener.java
index f8a9ed4..5112f60 100644
--- a/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaSubscribeListener.java
+++ b/taoyao-webrtc/taoyao-webrtc-mesh/src/main/java/com/acgist/taoyao/webrtc/mesh/listener/MediaSubscribeListener.java
@@ -6,17 +6,24 @@ import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.event.media.MediaSubscribeEvent;
import com.acgist.taoyao.signal.listener.MediaListenerAdapter;
+import lombok.extern.slf4j.Slf4j;
+
/**
* 订阅监听
*
* @author acgist
*/
+@Slf4j
public class MediaSubscribeListener extends MediaListenerAdapter {
@Override
public void onApplicationEvent(MediaSubscribeEvent event) {
final String sn = event.getSn();
final String to = event.getTo();
+ if(sn.equals(to)) {
+ log.debug("忽略订阅消息(相同终端):{}-{}", sn, to);
+ return;
+ }
final Message message = event.getMessage();
final Map mergeBody = event.mergeBody();
mergeBody.put("from", sn);