Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wecloud_im_server
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
hewei
wecloud_im_server
Commits
54fb1017
Commit
54fb1017
authored
Oct 18, 2021
by
giaogiao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
redis维护rtc频道
parent
2761ba3a
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
150 additions
and
495 deletions
+150
-495
common/src/main/java/com/wecloud/rtc/RtcRedisKey.java
+17
-25
common/src/main/java/com/wecloud/rtc/service/MangerRtcCacheService.java
+15
-6
common/src/main/java/com/wecloud/rtc/service/impl/MangerRtcCacheServiceImpl.java
+118
-0
common/src/main/java/com/wecloud/rtc/service/impl/MangerRtcChannelServiceImpl.java
+0
-103
docs/md/wecloud-im单人RTC对接文档的.md
+0
-361
No files found.
common/src/main/java/com/wecloud/rtc/RtcRedisKey.java
View file @
54fb1017
...
@@ -4,41 +4,33 @@ import java.io.Serializable;
...
@@ -4,41 +4,33 @@ import java.io.Serializable;
public
class
RtcRedisKey
implements
Serializable
{
public
class
RtcRedisKey
implements
Serializable
{
/**
* 维护频道信息
*/
public
static
final
String
RTC_CHANNEL_INFO
=
"rci:%s"
;
/**
/**
* 维护所有用户当前在线的频道ID
* 维护所有用户当前在线的频道ID
* new Map<String,Long> map
* map.put("clientA",10001)
* map.put("clientB",10001)
* map.put("clientC",10002)
* map.put("clientD",10003)
* <p>
* redis Key:
* user_join_channel = ujc
* user_join_channel = ujc
* rcu:clientA
:
10001
* rcu:clientA
=
10001
* rcu:clientB
:
10001
* rcu:clientB
=
10001
* rcu:clientC
:
10002
* rcu:clientC
=
10002
* rcu:clientD
:
10003
* rcu:clientD
=
10003
*/
*/
public
static
final
String
USER_JOIN_CHANNEL
=
"ujc:%s
:%s
"
;
public
static
final
String
USER_JOIN_CHANNEL
=
"ujc:%s"
;
/**
/**
* 维护频道中存在的用户
* 维护频道中存在的用户
* Map<Long,List<String>> map
* <p>
* new List<String> list
* list.add("clientA")
* list.add("clientB")
* <p>
* map.put(10001,list)
* <p>
* <p>
* redis Key:
* redis Key
(set 集合)
:
* rtc_channel_users = rcu
* rtc_channel_users = rcu
* key = rcu:10001:clientA
* rcu:10001 = clientA , clientB
* key = rcu:10001:clientB
* rcu:10002 = clientC
* key = rcu:10002:clientC
* rcu:10003 = clientD
* key = rcu:10003:clientD
*/
*/
public
static
final
String
RTC_CHANNEL_USERS
=
"rcu:%s
:%s
"
;
public
static
final
String
RTC_CHANNEL_USERS
=
"rcu:%s"
;
}
}
common/src/main/java/com/wecloud/rtc/service/MangerRtcC
hannel
Service.java
→
common/src/main/java/com/wecloud/rtc/service/MangerRtcC
ache
Service.java
View file @
54fb1017
package
com
.
wecloud
.
rtc
.
service
;
package
com
.
wecloud
.
rtc
.
service
;
import
com.fasterxml.jackson.core.JsonProcessingException
;
import
java.util.List
;
import
java.util.List
;
/**
/**
* 管理rtc频道
* 管理rtc频道
*/
*/
public
interface
MangerRtcChannelService
{
public
interface
MangerRtcCacheService
{
boolean
isEmpty
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
);
/**
/**
* 创建一个频道
* 创建一个频道
*
* @param appKey
* @param clientId
* @param rtcChannelId 雪花算法生成频道id
*/
*/
Long
create
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
;
void
create
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
throws
JsonProcessingException
;
/**
/**
* 加入频道
* 加入频道
...
@@ -27,13 +35,14 @@ public interface MangerRtcChannelService {
...
@@ -27,13 +35,14 @@ public interface MangerRtcChannelService {
*/
*/
List
<
String
>
getClientListByRtcChannelId
(
Long
rtcChannelId
);
List
<
String
>
getClientListByRtcChannelId
(
Long
rtcChannelId
);
/**
//
/**
* 根据客户端ID获取该客户端加入的频道ID
//
* 根据客户端ID获取该客户端加入的频道ID
*/
//
*/
Long
getRtcChannelIdListByClientId
(
String
appKey
,
String
clientId
);
//
Long getRtcChannelIdListByClientId(String appKey, String clientId);
/**
/**
* 获取客户端忙线/空闲状态
* 获取客户端忙线/空闲状态
*
* @param appKey
* @param appKey
* @param clientId
* @param clientId
* @return true:忙线,false空闲
* @return true:忙线,false空闲
...
...
common/src/main/java/com/wecloud/rtc/service/impl/MangerRtcCacheServiceImpl.java
0 → 100644
View file @
54fb1017
package
com
.
wecloud
.
rtc
.
service
.
impl
;
import
com.fasterxml.jackson.core.JsonProcessingException
;
import
com.fasterxml.jackson.databind.json.JsonMapper
;
import
com.wecloud.im.ws.utils.RedisUtils
;
import
com.wecloud.rtc.entity.RtcChannelInfo
;
import
com.wecloud.rtc.RtcRedisKey
;
import
com.wecloud.rtc.service.MangerRtcCacheService
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.util.ArrayList
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Set
;
@Service
public
class
MangerRtcCacheServiceImpl
implements
MangerRtcCacheService
{
@Autowired
private
RedisUtils
redisUtils
;
@Override
public
boolean
isEmpty
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
{
List
<
String
>
clientListByRtcChannelId
=
getClientListByRtcChannelId
(
rtcChannelId
);
// 移除自己
clientListByRtcChannelId
.
remove
(
appKey
+
clientId
);
return
clientListByRtcChannelId
.
isEmpty
();
}
@Override
public
void
create
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
throws
JsonProcessingException
{
// --- 频道信息
RtcChannelInfo
rtcChannelInfo
=
new
RtcChannelInfo
();
//当前房主
rtcChannelInfo
.
setOwner
(
appKey
+
clientId
);
//创建时间
rtcChannelInfo
.
setCreateTimestamp
(
new
Date
().
getTime
());
String
rtcChannelInfoJson
=
new
JsonMapper
().
writeValueAsString
(
rtcChannelInfo
);
// --- 保存频道信息
redisUtils
.
setKey
(
RtcRedisKey
.
RTC_CHANNEL_INFO
,
rtcChannelInfoJson
);
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
);
redisUtils
.
setKey
(
userJoinChannelKey
,
rtcChannelId
.
toString
());
//频道中存在的用户
String
rtcChannelUsers
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
);
redisUtils
.
addForSet
(
rtcChannelUsers
,
appKey
+
clientId
);
}
@Override
public
void
join
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
{
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
);
redisUtils
.
setKey
(
userJoinChannelKey
,
rtcChannelId
.
toString
());
//频道中存在的用户
String
rtcChannelUsers
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
);
redisUtils
.
addForSet
(
rtcChannelUsers
,
appKey
+
clientId
);
}
@Override
public
void
remove
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
{
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
);
redisUtils
.
delKey
(
userJoinChannelKey
);
//频道中存在的用户
String
rtcChannelUsers
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
);
redisUtils
.
removeForSet
(
rtcChannelUsers
,
appKey
+
clientId
);
}
@Override
public
List
<
String
>
getClientListByRtcChannelId
(
Long
rtcChannelId
)
{
//频道中存在的用户
String
rtcChannelUsers
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
);
Set
<
String
>
forSetMembers
=
redisUtils
.
getForSetMembers
(
rtcChannelUsers
);
return
new
ArrayList
<>(
forSetMembers
);
}
// @Override
// public Long getRtcChannelIdListByClientId(String appKey, String clientId) {
//
// //用户当前在线的频道ID
// String userJoinChannelKey = String.format(RtcRedisKey.USER_JOIN_CHANNEL, appKey + clientId);
// String key = redisUtils.getKey(userJoinChannelKey);
//
// return Long.valueOf(key);
// }
@Override
public
boolean
getBusyStatus
(
String
appKey
,
String
clientId
)
{
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
);
String
key
=
redisUtils
.
getKey
(
userJoinChannelKey
);
return
key
!=
null
&&
!
key
.
isEmpty
();
}
}
common/src/main/java/com/wecloud/rtc/service/impl/MangerRtcChannelServiceImpl.java
deleted
100644 → 0
View file @
2761ba3a
package
com
.
wecloud
.
rtc
.
service
.
impl
;
import
com.wecloud.im.ws.utils.RedisUtils
;
import
com.wecloud.rtc.RtcRedisKey
;
import
com.wecloud.rtc.service.MangerRtcChannelService
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Set
;
@Service
public
class
MangerRtcChannelServiceImpl
implements
MangerRtcChannelService
{
@Autowired
private
RedisUtils
redisUtils
;
@Override
public
Long
create
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
{
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
,
rtcChannelId
);
//频道中存在的用户
String
rtcChannelUsersKey
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
,
appKey
+
clientId
);
redisUtils
.
setKey
(
userJoinChannelKey
,
""
);
redisUtils
.
setKey
(
rtcChannelUsersKey
,
""
);
return
null
;
}
@Override
public
void
join
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
{
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
,
rtcChannelId
);
//频道中存在的用户
String
rtcChannelUsersKey
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
,
appKey
+
clientId
);
redisUtils
.
setKey
(
userJoinChannelKey
,
""
);
redisUtils
.
setKey
(
rtcChannelUsersKey
,
""
);
}
@Override
public
void
remove
(
String
appKey
,
String
clientId
,
Long
rtcChannelId
)
{
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
,
rtcChannelId
);
//频道中存在的用户
String
rtcChannelUsersKey
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
,
appKey
+
clientId
);
redisUtils
.
delKey
(
userJoinChannelKey
);
redisUtils
.
delKey
(
rtcChannelUsersKey
);
}
@Override
public
List
<
String
>
getClientListByRtcChannelId
(
Long
rtcChannelId
)
{
String
rtcChannelUsersKey
=
String
.
format
(
RtcRedisKey
.
RTC_CHANNEL_USERS
,
rtcChannelId
,
"*"
);
Set
<
String
>
keys
=
redisUtils
.
keys
(
rtcChannelUsersKey
);
List
<
String
>
clientList
=
new
ArrayList
<>();
for
(
String
next:
keys
){
String
s
=
next
.
split
(
":"
)[
2
];
clientList
.
add
(
s
);
}
return
clientList
;
}
@Override
public
Long
getRtcChannelIdListByClientId
(
String
appKey
,
String
clientId
)
{
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
,
"*"
);
Set
<
String
>
keys
=
redisUtils
.
keys
(
userJoinChannelKey
);
String
next
=
keys
.
iterator
().
next
();
return
Long
.
valueOf
(
next
.
split
(
":"
)[
2
]);
}
@Override
public
boolean
getBusyStatus
(
String
appKey
,
String
clientId
)
{
//用户当前在线的频道ID
String
userJoinChannelKey
=
String
.
format
(
RtcRedisKey
.
USER_JOIN_CHANNEL
,
appKey
+
clientId
,
"*"
);
Set
<
String
>
keys
=
redisUtils
.
keys
(
userJoinChannelKey
);
if
(
keys
==
null
||
keys
.
isEmpty
())
{
return
false
;
}
else
{
return
true
;
}
}
}
docs/md/wecloud-im单人RTC对接文档的.md
deleted
100644 → 0
View file @
2761ba3a
# wecloud-RTC音视频客户端信令对接文档
# wecloud-RTC音视频客户端信令对接文档
## 文档描述
此文档为单人RTC音视频通讯技术对接文档
由于RTC基于wecloud-im即时通讯服务,
**对接RTC前,需要先对接wecloud-im服务**
## 核心概念说明
### 频道与会话
频道与会话的概念不一样
```
1. 引入"频道RtcChannel"概念, 可以不基于会话发起音视频通话
2. 目前频道只支持两个client,进行通话的两端必须先加入到同一个"频道",所有的指令都在频道内进行转发
3. 允许不在同个会话中的两个client加入到同个频道进行通话 (可配置是否两个client必须在同一个会话才能发起音视频通话)
4. 一个频道可以由通话发起者绑定到会话ID,"挂断","未接听"等状态会同步到会话, 未绑定将不同步到会话(可选)
5. 连接websocket时带上client类型, 如web,安卓,ios
client需要监听频道内 状态更新(房间断开,挂断)、用户状态更新(用户加入, 用户退出)、流状态更新(切换音频 切换视频)
```
## 流程图

## 指令码说明
#### **subCmd**子类型指令码
##### 客户端**请求**指令列表:
**create**
:创建频道
**join**
: 加入频道
**SDP**
:SDP数据转发
**reject**
:拒绝加入频道
**leave**
:主动挂断(离开频道)
##### 服务端**响应**指令列表:
**rtcCall**
:接收到RTC邀请
**clientEvent**
: 用户状态更新事件(用户加入,用户退出,用户拒接邀请)
**typeEvent**
:流状态更新(切换音频 切换视频)
**statusEvent**
:状态更新(网络断开,挂断)
**SDP**
:SDP数据转发
**busy**
:忙线
## 创建频道 发起RTC音视频通话
### 1.client发起方 发起请求:
**示例一: 必传参数:**
```
json
{
"reqId"
:
"555111-ad-afd12"
,
"cmd"
:
3
,
"data"
:{
"toClient"
:[
"client_3030"
],
"subCmd"
:
"create"
,
"type"
:
"video"
}
}
```
**示例二: 全部可选参数:**
```
json
{
"reqId"
:
"555111-ad-afd12"
,
"cmd"
:
3
,
"data"
:{
"diyParam自定义字段"
:
"aaaa自已定义字段的值"
,
"toConversation"
:
null
,
"toClient"
:[
"client_3030"
],
"subCmd"
:
"create"
,
"type"
:
"video"
,
"push"
:{
"title"
:
"xxx正在邀请你视频通话"
,
"subTitle"
:
"点击接听"
},
"attrs"
:{
"a"
:
"示例: 用户自定义的一些键值对A"
,
"b"
:
"示例: 存储用户自定义的一些键值对B"
}
}
}
```
**说明:**
toConversation会话ID可以为空, 为空时通话记录将不会记录到会话当中, 如"未接听", "已拒绝"等事件通知不会写入到会话的聊天记录中.
**请求参数描述**
| 字段名 | 字段类型 | 是否可空 | 说明 |
| ---- | -------- | -- | ------ |
| cmd | String | 否 | 指令码 |
| attrs | Object | 是 | 自定义拓展字段 |
| toConversation | Long | 是 | 会话id,可为空 |
| diyParam自定义字段 | Object | 是 | 自定义拓展字段 |
| push | String | 是 | 接收方展示的系统推送内容 |
| subCmd | String | 否 | 子类型指令 |
| toClient | Array
[
String
]
| 否 | 被邀请的客户端ID |
| type | String | 否 | 类型: "video" 或 "voice" |
### 2.服务端向client发起方响应数据:
```
json
{
"reqId"
:
"555111-ad-afd12"
,
"cmd"
:
5
,
"data"
:{
"channelId"
:
1234263457652
}
}
```
**参数描述**
| 字段名 | 字段类型 | 是否可空 | 说明 |
| ---- | -------- | -- | ------ |
| cmd | String | 否 | 指令码 |
| channelId | Long | 否 | 由服务端创建的频道id |
## 接收方收到RTC音视频通话邀请
服务端向client接收方下发数据:
```
json
{
"cmd"
:
4
,
"data"
:{
"subCmd"
:
"rtcCall"
,
"subData"
:{
"type"
:
"video"
,
"toConversation"
:
null
,
"channelId"
:
1234263457652
,
"sender"
:
"client_1010"
},
"diyParam自定义字段"
:
"aaaa自已定义字段的值"
,
"attrs"
:{
"a"
:
"示例: 用户自定义的一些键值对"
,
"b"
:
"存储用户自定义的一些键值对"
}
}
}
```
**参数描述**
| 字段名 | 字段类型 | 是否可空 | 说明 |
| ---- | -------- | -- | ------ |
| cmd | String | 否 | 指令码 |
| attrs | Object | 是 | 自定义拓展字段 |
| toConversation | Long | 否 | 绑定的会话id |
| diyParam自定义字段 | Object | 是 | 自定义拓展字段 |
| subCmd | String | 否 | 子类型指令 |
| sender | String | 否 | 发起通话的客户端ID |
| channelId | Long | 否 | 由服务端创建的频道id |
| type | String | 否 | 类型: "video" 或 "voice" |
## 同意加入频道
client接收方向服务端请求数据:
```
json
{
"reqId"
:
"123"
,
"cmd"
:
3
,
"data"
:{
"channelId"
:
1234263457652
,
"subCmd"
:
"join"
}
}
```
**参数描述**
| 字段名 | 字段类型 | 是否可空 | 说明 |
| ---- | -------- | -- | ------ |
| cmd | String | 否 | 指令码 |
| attrs | Object | 是 | 自定义拓展字段 |
| diyParam自定义字段 | Object | 是 | 自定义拓展字段 |
| subCmd | String | 否 | 子类型指令 |
## 通知:有client加入频道
服务端向频道内其他client响应数据:
```
json
{
"cmd"
:
4
,
"data"
:{
"subCmd"
:
"clientEvent"
,
"subData"
:{
"channelId"
:
1234263457652
,
"type"
:
"join"
,
"clientId"
:
7657567
,
}
}
}
```
## 拒绝加入频道
client接收方向服务端请求数据:
```
json
{
"reqId"
:
"123"
,
"cmd"
:
3
,
"data"
:{
"subCmd"
:
"reject"
,
"diyParam自定义字段"
:
"aaaa自已定义字段的值"
,
"channelId"
:
1234263457652
,
"attrs"
:{}
}
}
```
**说明:**
**参数描述**
| 字段名 | 字段类型 | 是否可空 | 说明 |
| ---- | -------- | -- | ------ |
| cmd | String | 否 | 指令码 |
| attrs | Object | 是 | 自定义拓展字段 |
| toConversation | Long | 否 | 会话id |
| diyParam自定义字段 | Object | 是 | 自定义拓展字段 |
| subCmd | String | 否 | 子类型 |
## 通知:有Client拒绝加入频道
服务端向频道内其他client响应数据:
```
json
{
"cmd"
:
4
,
"data"
:{
"subCmd"
:
"clientEvent"
,
"subData"
:{
"channelId"
:
1234263457652
,
"type"
:
"reject"
,
"clientId"
:
7657567
,
}
}
}
```
## 流媒体描述信息SDP转发
(服务端仅负责转发)(ice_candidate,anser,offer)
### client发送SDP
```
json
{
"reqId"
:
"123"
,
"cmd"
:
3
,
"data"
:{
"subCmd"
:
"sdp"
,
"channelId"
:
1234263457652
,
"sdpData"
:
"xxxxxxxxxxxxxxxx"
,
"diyParam自定义字段"
:
"aaaa自已定义字段的值"
,
"attrs"
:{}
}
}
```
### client接收SDP
```
json
{
"cmd"
:
4
,
"data"
:{
"subCmd"
:
"sdp"
,
"subData"
:{
"channelId"
:
1234263457652
,
"clientId"
:
7657567
,
"sdpData"
:
"xxxxxxxxxxxxxxxx"
,
"diyParam自定义字段"
:
"aaaa自已定义字段的值"
,
"attrs"
:{}
}
}
}
```
## 主动挂断(离开频道)
client主动离开频道
向服务端请求:
```
json
{
"reqId"
:
"123"
,
"cmd"
:
3
,
"data"
:{
"subCmd"
:
"leave"
,
"channelId"
:
1234263457652
}
}
```
## 通知:有client离开频道
服务端向频道内其他client响应数据:
```
json
{
"cmd"
:
4
,
"data"
:{
"subCmd"
:
"clientEvent"
,
"subData"
:{
"channelId"
:
1234263457652
,
"type"
:
"leave"
,
"clientId"
:
7657567
,
}
}
}
```
## 对方忙线(对方正在通话中)
服务端响应给发起方
```
json
{
"reqId"
:
"555111-ad-afd12"
,
"cmd"
:
5
,
"data"
:{
"subCmd"
:
"busy"
,
"subData"
:{
"channelId"
:
1234263457652
,
"clientId"
:
7657567
}
}
}
```
## 断线重连
重新join进频道即可重连
## 对方是否还挂起
## 视频/音频切换
## 查询对方是否离开
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment