[*] 删除房间

This commit is contained in:
acgist
2023-02-05 11:26:06 +08:00
parent 50ee82779e
commit 17218c05d7
27 changed files with 140 additions and 499 deletions

View File

@@ -1,6 +1,6 @@
# 桃夭 # 桃夭
桃夭是套`WebRTC`信令服务,使用`Mediasoup`提供媒体服务,支持直播会议两种场景。 桃夭是套基于`Mediasoup`开发的`WebRTC`音视频信令服务
## 模块 ## 模块
@@ -8,7 +8,7 @@
|:--|:--|:--| |:--|:--|:--|
|taoyao-client|终端示例|Web端终端示例| |taoyao-client|终端示例|Web端终端示例|
|taoyao-media-server|媒体服务|Mediasoup媒体服务| |taoyao-media-server|媒体服务|Mediasoup媒体服务|
|taoyao-signal-server|信令服务|直播会议业务逻辑| |taoyao-signal-server|信令服务|信令业务逻辑|
## 部署 ## 部署
@@ -44,7 +44,7 @@
* 录制Recorder * 录制Recorder
* 音频:降噪、混音、变声 * 音频:降噪、混音、变声
* 视频水印、美颜、AI识别 * 视频水印、美颜、AI识别
* 一个信令服务多个媒体服务
* 信令服务集群
* 信令直传 * 信令直传
* 信令服务集群
* 媒体交互式启动 * 媒体交互式启动
* 一个信令服务多个媒体服务

View File

@@ -30,7 +30,7 @@ Signal -> ClientA: 关闭ClientA通道
deactivate ClientA deactivate ClientA
Signal -> ClientB: ClientA下线 Signal -> ClientB: ClientA下线
Signal -> ClientC: ClientA下线 Signal -> ClientC: ClientA下线
Signal -> Signal: 释放ClientA资源直播、会议、媒体) Signal -> Signal: 释放ClientA资源会议、媒体
deactivate Signal deactivate Signal
@enduml @enduml

View File

@@ -1,32 +0,0 @@
@startuml
title WebRTC-Mesh
actor ClientA as ClientA
participant "Signal" as Signal
actor ClientB as ClientB
actor ClientC as ClientC
autonumber
ClientA -> Signal: 进入房间
activate ClientA
activate Signal
Signal -> ClientB: ClientA进入房间
activate ClientB
ClientB -> Signal: 订阅ClientA
Signal -> ClientA: ClientB订阅ClientA
ClientA -> Signal: ClientA发布ClientB
Signal -> ClientB: ClientA发布
deactivate ClientB
Signal -> ClientC: ClientA进入房间
activate ClientC
ClientC -> Signal: 订阅ClientA
Signal -> ClientA: ClientC订阅ClientA
ClientA -> Signal: ClientA发布ClientC
Signal -> ClientC: ClientA发布
deactivate ClientC
deactivate Signal
deactivate ClientA
@enduml

View File

@@ -1,32 +0,0 @@
@startuml
title WebRTC-Mesh
actor ClientA as ClientA
participant "Signal" as Signal
actor ClientB as ClientB
actor ClientC as ClientC
autonumber
ClientA -> Signal: 进入房间
activate ClientA
activate Signal
Signal -> ClientB: ClientA进入房间
activate ClientB
ClientB -> Signal: 订阅ClientA
Signal -> ClientA: ClientB订阅ClientA
ClientA -> Signal: ClientA发布ClientB
Signal -> ClientB: ClientA发布
deactivate ClientB
Signal -> ClientC: ClientA进入房间
activate ClientC
ClientC -> Signal: 订阅ClientA
Signal -> ClientA: ClientC订阅ClientA
ClientA -> Signal: ClientA发布ClientC
Signal -> ClientC: ClientA发布
deactivate ClientC
deactivate Signal
deactivate ClientA
@enduml

View File

@@ -1,34 +0,0 @@
@startuml
title WebRTC-Mesh
actor ClientA as ClientA
participant "Signal" as Signal
actor ClientB as ClientB
actor ClientC as ClientC
autonumber
ClientA -> Signal: 进入房间
activate ClientA
activate Signal
Signal -> ClientB: ClientA进入房间
activate ClientB
ClientB -> Signal: 订阅ClientA
Signal -> ClientA: ClientB订阅ClientA
ClientA --> Signal: ClientA发布ClientB
Signal --> ClientB: ClientA发布
deactivate ClientB
Signal -> ClientC: ClientA进入房间
activate ClientC
ClientC -> Signal: 订阅ClientA
Signal -> ClientA: ClientC订阅ClientA
ClientA --> Signal: ClientA发布ClientC
Signal --> ClientC: ClientA发布
deactivate ClientC
deactivate Signal
deactivate ClientA
@enduml

View File

@@ -1,32 +0,0 @@
@startuml
title WebRTC-Mesh
actor ClientA as ClientA
participant "Signal" as Signal
actor ClientB as ClientB
actor ClientC as ClientC
autonumber
ClientA -> Signal: 进入房间
activate ClientA
activate Signal
Signal -> ClientB: ClientA进入房间
activate ClientB
ClientB -> Signal: 订阅ClientA
Signal -> ClientA: ClientB订阅ClientA
ClientA -> Signal: ClientA发布ClientB
Signal -> ClientB: ClientA发布
deactivate ClientB
Signal -> ClientC: ClientA进入房间
activate ClientC
ClientC -> Signal: 订阅ClientA
Signal -> ClientA: ClientC订阅ClientA
ClientA -> Signal: ClientA发布ClientC
Signal -> ClientC: ClientA发布
deactivate ClientC
deactivate Signal
deactivate ClientA
@enduml

View File

@@ -21,9 +21,6 @@ input[type=text]:focus,textarea:focus,input[type=text]:hover,textarea:hover{bord
input::-webkit-calendar-picker-indicator{color:#1155AA;background:none;} input::-webkit-calendar-picker-indicator{color:#1155AA;background:none;}
/**容器*/ /**容器*/
.taoyao{text-align:center;} .taoyao{text-align:center;}
/**直播*/
.taoyao .live > .video{width:100%;height:100%;}
.taoyao .live .handler{position:fixed;width:100%;bottom:2rem;font-size:2rem;}
/**会议*/ /**会议*/
.taoyao .handler a{cursor:pointer;} .taoyao .handler a{cursor:pointer;}
.taoyao > .handler{font-size:2rem;padding:1rem 0;width:100%;} .taoyao > .handler{font-size:2rem;padding:1rem 0;width:100%;}

View File

@@ -2,17 +2,127 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>桃夭</title> <title>会议</title>
<link rel="stylesheet" type="text/css" href="./css/font.min.css" />
<link rel="stylesheet" type="text/css" href="./css/style.css" /> <link rel="stylesheet" type="text/css" href="./css/style.css" />
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script type="text/javascript" src="./javascript/taoyao.js"></script> <script type="text/javascript" src="./javascript/taoyao.js"></script>
<style type="text/css">
a{width:50%;height:100%;position:fixed;text-align:center;line-height:100%;font-size:4rem;display:flex;align-items:center;justify-content:center;}
a:last-child{left:50%;}
a:hover{color:#fff;background:#060;}
</style>
</head> </head>
<body> <body>
<a href="./live.html">直播</a> <div class="taoyao" id="app">
<a href="./meeting.html">会议</a> <div class="handler">
<a class="create icon-make-group" title="创建会议" @click="create"></a>
<a class="invite icon-address-book" title="邀请会议" @click="invite"></a>
<a class="enter icon-enter" title="进入会议" @click="enter"></a>
<a class="leave icon-exit" title="离开会议" @click="leave"></a>
<a class="close icon-switch" title="关闭会议" @click="close"></a>
</div>
<div class="list">
<div class="meeting me">
<div class="video">
<video id="local"></video>
</div>
<div class="handler">
<a class="audio icon-volume-medium" title="音频状态" @click="audioSelf"></a>
<a class="video icon-play2" title="视频状态" @click="videoSelf"></a>
<a class="record icon-radio-checked" title="录制视频" @click="recordSelf"></a>
</div>
</div>
<div class="meeting" v-for="client in this.remoteClient" :key="client.sn">
<div class="video">
<video v-bind:id="client.sn"></video>
</div>
<div class="handler">
<a class="audio" title="音频状态" v-bind:class="client.audioStatus?'icon-volume-medium':'icon-volume-mute2'" @click="audio(client.sn)"></a>
<a class="video" title="视频状态" v-bind:class="client.videoStatus?'icon-play2':'icon-stop'" @click="video(client.sn)"></a>
<a class="record icon-radio-checked" title="录制视频" v-bind:class="client.recordStatus?'active':''" @click="record(client.sn)"></a>
<a class="expel icon-cancel-circle" title="踢出会议" @click="expel(client.sn)"></a>
</div>
</div>
</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: "#app",
data: {
taoyao: null,
remoteClient: [],
meetingId: null
},
mounted() {
// 设置帐号
let sn = prompt('你的账号', signalConfig.sn);
signalConfig.sn = sn;
// 加载桃夭
let self = this;
this.taoyao = new Taoyao('local');
this.remoteClient = this.taoyao.remoteClient;
// 打开信令通道
this.taoyao
.buildChannel(self.callback)
.then(e => console.debug('信令通道连接成功'));
},
beforeDestroy() {
},
methods: {
// 信令回调true表示已经处理false表示没有处理
callback: function(data) {
let self = this;
switch(data.header.pid) {
}
return false;
},
// 创建会议
create: function(event) {
let self = this;
this.taoyao.meetingCreate(data => {
self.taoyao.meetingEnter(data.body.id);
return true;
});
},
// 会议邀请
invite: function(sn) {
},
// 进入会议
enter: function(sn) {
let id = prompt('房间标识');
let password = prompt('房间密码');
if(id) {
this.taoyao.meetingEnter(id);
}
},
// 离开会议
leave: function(sn) {
},
// 关闭会议
close: function(sn) {
},
// 控制音频
audio: function(sn) {
this.client(sn).audio = !this.client(sn).audio;
},
// 控制视频
video: function(sn) {
this.client(sn).video = !this.client(sn).video;
},
// 录制视频
record: function(sn) {
this.client(sn).record = !this.client(sn).record;
},
// 踢出会议
expel: function(sn) {
},
// 控制音频
audioSelf: function(sn) {
},
// 控制视频
videoSelf: function(sn) {
},
// 录制视频
recordSelf: function(sn) {
}
}
});
</script>
</body> </body>
</html> </html>

View File

@@ -69,9 +69,6 @@ const signalConfig = {
}; };
/** 信令协议 */ /** 信令协议 */
const signalProtocol = { const signalProtocol = {
/** 直播信令 */
live: {
},
/** 媒体信令 */ /** 媒体信令 */
media: { media: {
/** 发布 */ /** 发布 */

View File

@@ -1,28 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>直播</title>
<link rel="stylesheet" type="text/css" href="./css/font.min.css" />
<link rel="stylesheet" type="text/css" href="./css/style.css" />
<script type="text/javascript" src="./javascript/taoyao.js"></script>
</head>
<body>
<div class="taoyao" id="taoyao">
<div class="live">
<div class="video">
</div>
<div class="handler">
<a class="audio icon-volume-medium" title="音频状态"></a>
<a class="video icon-play2" title="视频状态"></a>
<a class="record icon-radio-checked" title="录制视频"></a>
<a class="kick icon-cancel-circle" title="退出直播"></a>
<a class="close icon-switch" title="关闭直播"></a>
</div>
</div>
</div>
<script type="text/javascript">
const live = document.getElementById('taoyao');
</script>
</body>
</html>

View File

@@ -1,128 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>会议</title>
<link rel="stylesheet" type="text/css" href="./css/font.min.css" />
<link rel="stylesheet" type="text/css" href="./css/style.css" />
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script type="text/javascript" src="./javascript/taoyao.js"></script>
</head>
<body>
<div class="taoyao" id="app">
<div class="handler">
<a class="create icon-make-group" title="创建会议" @click="create"></a>
<a class="invite icon-address-book" title="邀请会议" @click="invite"></a>
<a class="enter icon-enter" title="进入会议" @click="enter"></a>
<a class="leave icon-exit" title="离开会议" @click="leave"></a>
<a class="close icon-switch" title="关闭会议" @click="close"></a>
</div>
<div class="list">
<div class="meeting me">
<div class="video">
<video id="local"></video>
</div>
<div class="handler">
<a class="audio icon-volume-medium" title="音频状态" @click="audioSelf"></a>
<a class="video icon-play2" title="视频状态" @click="videoSelf"></a>
<a class="record icon-radio-checked" title="录制视频" @click="recordSelf"></a>
</div>
</div>
<div class="meeting" v-for="client in this.remoteClient" :key="client.sn">
<div class="video">
<video v-bind:id="client.sn"></video>
</div>
<div class="handler">
<a class="audio" title="音频状态" v-bind:class="client.audioStatus?'icon-volume-medium':'icon-volume-mute2'" @click="audio(client.sn)"></a>
<a class="video" title="视频状态" v-bind:class="client.videoStatus?'icon-play2':'icon-stop'" @click="video(client.sn)"></a>
<a class="record icon-radio-checked" title="录制视频" v-bind:class="client.recordStatus?'active':''" @click="record(client.sn)"></a>
<a class="expel icon-cancel-circle" title="踢出会议" @click="expel(client.sn)"></a>
</div>
</div>
</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: "#app",
data: {
taoyao: null,
remoteClient: [],
meetingId: null
},
mounted() {
// 设置帐号
let sn = prompt('你的账号', signalConfig.sn);
signalConfig.sn = sn;
// 加载桃夭
let self = this;
this.taoyao = new Taoyao('local');
this.remoteClient = this.taoyao.remoteClient;
// 打开信令通道
this.taoyao
.buildChannel(self.callback)
.then(e => console.debug('信令通道连接成功'));
},
beforeDestroy() {
},
methods: {
// 信令回调true表示已经处理false表示没有处理
callback: function(data) {
let self = this;
switch(data.header.pid) {
}
return false;
},
// 创建会议
create: function(event) {
let self = this;
this.taoyao.meetingCreate(data => {
self.taoyao.meetingEnter(data.body.id);
return true;
});
},
// 会议邀请
invite: function(sn) {
},
// 进入会议
enter: function(sn) {
let id = prompt('房间标识');
let password = prompt('房间密码');
if(id) {
this.taoyao.meetingEnter(id);
}
},
// 离开会议
leave: function(sn) {
},
// 关闭会议
close: function(sn) {
},
// 控制音频
audio: function(sn) {
this.client(sn).audio = !this.client(sn).audio;
},
// 控制视频
video: function(sn) {
this.client(sn).video = !this.client(sn).video;
},
// 录制视频
record: function(sn) {
this.client(sn).record = !this.client(sn).record;
},
// 踢出会议
expel: function(sn) {
},
// 控制音频
audioSelf: function(sn) {
},
// 控制视频
videoSelf: function(sn) {
},
// 录制视频
recordSelf: function(sn) {
}
}
});
</script>
</body>
</html>

View File

@@ -11,13 +11,21 @@
|taoyao-signal|信令|信令服务| |taoyao-signal|信令|信令服务|
|taoyao-server|服务|启动服务| |taoyao-server|服务|启动服务|
### 直播 ### 会议模式
直播、连麦、监控、视频同看 所有人员均能视频通话对讲
### 会议 ### 对讲模式
会议模式、广播模式、单人对讲 两个人员之间对讲
### 广播模式
单个人员能够讲话并且能够听到其他人员讲话,其他人员之间不能讲话。
### 本地视频模式
使用本地文件作为音频视频信息
## 模块关系 ## 模块关系

View File

@@ -17,7 +17,7 @@
<url>https://gitee.com/acgist/taoyao</url> <url>https://gitee.com/acgist/taoyao</url>
<name>taoyao</name> <name>taoyao</name>
<description>桃夭:桃夭是套`WebRTC`信令服务,使用`Mediasoup`提供媒体服务,支持直播会议两种场景。</description> <description>桃夭:桃夭是套基于`Mediasoup`开发的`WebRTC`音视频信令服务</description>
<inceptionYear>2022</inceptionYear> <inceptionYear>2022</inceptionYear>
<properties> <properties>

View File

@@ -37,15 +37,6 @@ public class SpringDocAutoConfiguration {
@Autowired @Autowired
private TaoyaoProperties taoyaoProperties; private TaoyaoProperties taoyaoProperties;
@Bean
public GroupedOpenApi liveApi() {
return GroupedOpenApi.builder()
.group("live")
.displayName("直播")
.packagesToScan("com.acgist.taoyao.live")
.build();
}
@Bean @Bean
public GroupedOpenApi meetingApi() { public GroupedOpenApi meetingApi() {
return GroupedOpenApi.builder() return GroupedOpenApi.builder()

View File

@@ -1,59 +0,0 @@
package com.acgist.taoyao.media.live;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* 直播
*
* @author acgist
*/
@Getter
@Setter
@Schema(title = "直播", description = "直播")
public class Live {
/**
* 直播标识
*/
@Schema(title = "直播标识", description = "直播标识")
private String id;
/**
* 直播名称
*/
@Schema(title = "直播名称", description = "直播名称")
private String name;
/**
* 直播密码
*/
@Schema(title = "直播密码", description = "直播密码")
private String password;
/**
* 终端会话标识列表
*/
@Schema(title = "终端会话标识列表", description = "终端会话标识列表")
private List<String> sns;
/**
* 创建终端标识
*/
@Schema(title = "创建终端标识", description = "创建终端标识")
private String creator;
/**
* 新增终端会话标识
*
* @param sn 终端会话标识
*/
public void addSn(String sn) {
synchronized (this.sns) {
if(this.sns.contains(sn)) {
return;
}
this.sns.add(sn);
}
}
}

View File

@@ -1,20 +0,0 @@
package com.acgist.taoyao.media.live;
import org.springframework.beans.factory.annotation.Autowired;
import com.acgist.taoyao.signal.event.ApplicationEventAdapter;
import com.acgist.taoyao.signal.listener.ApplicationListenerAdapter;
/**
* 直播事件监听适配器
*
* @param <E> 事件泛型
*
* @author acgist
*/
public abstract class LiveListenerAdapter<E extends ApplicationEventAdapter> extends ApplicationListenerAdapter<E> {
@Autowired
protected LiveManager liveManager;
}

View File

@@ -1,13 +0,0 @@
package com.acgist.taoyao.media.live;
import com.acgist.taoyao.boot.annotation.Manager;
/**
* 直播管理
*
* @author acgist
*/
@Manager
public class LiveManager {
}

View File

@@ -1,46 +0,0 @@
package com.acgist.taoyao.media.live.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.media.live.Live;
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;
/**
* 直播
*
* @author acgist
*/
@Tag(name = "直播", description = "直播管理")
@RestController
@RequestMapping("/live")
public class LiveController {
@Operation(summary = "直播列表", description = "直播列表")
@GetMapping("/list")
@ApiResponse(content = @Content(schema = @Schema(implementation = Live.class)))
public Message list() {
return Message.success();
}
@Operation(summary = "直播状态", description = "直播状态")
@GetMapping("/status/{id}")
public Message status(@PathVariable String id) {
return Message.success();
}
@Operation(summary = "直播终端列表", description = "直播终端列表")
@GetMapping("/list/client")
public Message listClient() {
return Message.success();
}
}

View File

@@ -281,20 +281,6 @@
响应所有终端状态列表 响应所有终端状态列表
## 直播信令3000~3999
### 开启直播信令3000
### 关闭直播信令3001
### 直播广播信令3002
### 直播终端列表信令3997
### 直播状态信令3998
### 直播列表信令3999
## 会议信令4000~4999 ## 会议信令4000~4999
### 创建会议信令4000 ### 创建会议信令4000

View File

@@ -14,7 +14,7 @@ import com.acgist.taoyao.signal.protocol.client.ClientOnlineProtocol;
/** /**
* 终端注册监听 * 终端注册监听
* *
* TODO如果已经在会议、直播中,自动推流。 * TODO如果已经在会议中自动推流。
* *
* @author acgist * @author acgist
*/ */

View File

@@ -8,10 +8,11 @@ import com.acgist.taoyao.signal.event.ApplicationEventAdapter;
/** /**
* 信令 * 信令
* *
* TODO改为字符
*
* 1000~1999平台信令 * 1000~1999平台信令
* 2000~2999终端信令 * 2000~2999终端信令
* 3000~3999会议信令 * 3000~3999会议信令
* 4000~4999直播信令
* 5000~5999媒体信令 * 5000~5999媒体信令
* 6000~6999媒体信令Mediasoup * 6000~6999媒体信令Mediasoup
* *

View File

@@ -1,5 +0,0 @@
package com.acgist.taoyao.signal.protocol.live;
public class LiveClientListProtocol {
}

View File

@@ -1,5 +0,0 @@
package com.acgist.taoyao.signal.protocol.live;
public class LiveCloseProtocol {
}

View File

@@ -1,5 +0,0 @@
package com.acgist.taoyao.signal.protocol.live;
public class LiveListProtocol {
}

View File

@@ -1,5 +0,0 @@
package com.acgist.taoyao.signal.protocol.live;
public class LiveRegisterProtocol {
}

View File

@@ -1,5 +0,0 @@
package com.acgist.taoyao.signal.protocol.live;
public class LiveStatusProtocol {
}