Переглянути джерело

feat:多人视频-开发中

xsx 1 рік тому
батько
коміт
32e79bcb08

+ 3 - 1
im-platform/src/main/java/com/bx/implatform/config/ICEServerConfig.java → im-platform/src/main/java/com/bx/implatform/config/WebrtcConfig.java

@@ -10,7 +10,9 @@ import java.util.List;
 @Data
 @Component
 @ConfigurationProperties(prefix = "webrtc")
-public class ICEServerConfig {
+public class WebrtcConfig {
+
+    private Integer maxChannel = 9;
 
     private List<ICEServer> iceServers = new ArrayList<>();
 

+ 7 - 0
im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java

@@ -1,5 +1,6 @@
 package com.bx.implatform.controller;
 
+import com.bx.implatform.config.WebrtcConfig;
 import com.bx.implatform.dto.*;
 import com.bx.implatform.result.Result;
 import com.bx.implatform.result.ResultUtils;
@@ -121,4 +122,10 @@ public class WebrtcGroupController {
         webrtcGroupService.heartbeat(groupId);
         return ResultUtils.success();
     }
+
+    @GetMapping("/config")
+    @ApiOperation(httpMethod = "GET", value = "获取系统配置")
+    public Result<WebrtcConfig> loadConfig() {
+        return ResultUtils.success(webrtcGroupService.loadConfig());
+    }
 }

+ 7 - 0
im-platform/src/main/java/com/bx/implatform/service/IWebrtcGroupService.java

@@ -1,5 +1,6 @@
 package com.bx.implatform.service;
 
+import com.bx.implatform.config.WebrtcConfig;
 import com.bx.implatform.dto.*;
 import com.bx.implatform.vo.WebrtcGroupInfoVO;
 
@@ -75,4 +76,10 @@ public interface IWebrtcGroupService {
      */
     void heartbeat(Long groupId);
 
+
+    /**
+     * 加载配置
+     */
+    WebrtcConfig loadConfig();
+
 }

+ 85 - 33
im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java

@@ -6,28 +6,31 @@ import com.bx.imclient.IMClient;
 import com.bx.imcommon.model.IMGroupMessage;
 import com.bx.imcommon.model.IMUserInfo;
 import com.bx.implatform.annotation.RedisLock;
+import com.bx.implatform.config.WebrtcConfig;
 import com.bx.implatform.contant.RedisKey;
 import com.bx.implatform.dto.*;
 import com.bx.implatform.entity.GroupMember;
+import com.bx.implatform.entity.GroupMessage;
+import com.bx.implatform.enums.MessageStatus;
 import com.bx.implatform.enums.MessageType;
 import com.bx.implatform.exception.GlobalException;
 import com.bx.implatform.service.IGroupMemberService;
+import com.bx.implatform.service.IGroupMessageService;
 import com.bx.implatform.service.IWebrtcGroupService;
 import com.bx.implatform.session.SessionContext;
 import com.bx.implatform.session.UserSession;
 import com.bx.implatform.session.WebrtcGroupSession;
 import com.bx.implatform.session.WebrtcUserInfo;
+import com.bx.implatform.util.BeanUtils;
 import com.bx.implatform.util.UserStateUtils;
 import com.bx.implatform.vo.GroupMessageVO;
 import com.bx.implatform.vo.WebrtcGroupFailedVO;
 import com.bx.implatform.vo.WebrtcGroupInfoVO;
-import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
-import java.lang.reflect.Member;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -45,26 +48,28 @@ import java.util.stream.Collectors;
 public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
 
     private final IGroupMemberService groupMemberService;
+    private final IGroupMessageService groupMessageService;
     private final RedisTemplate<String, Object> redisTemplate;
     private final IMClient imClient;
     private final UserStateUtils userStateUtils;
-    /**
-     * 最多支持8路视频
-     */
-    private final int maxChannel = 9;
+    private final WebrtcConfig webrtcConfig;
+
 
     @RedisLock(prefixKey = RedisKey.IM_LOCK_RTC_GROUP, key = "#dto.groupId")
     @Override
     public void setup(WebrtcGroupSetupDTO dto) {
         UserSession userSession = SessionContext.getSession();
+        if (dto.getUserInfos().size() > webrtcConfig.getMaxChannel()) {
+            throw new GlobalException("最多支持" + webrtcConfig.getMaxChannel() + "人进行通话");
+        }
         List<Long> userIds = getRecvIds(dto.getUserInfos());
+        if (!groupMemberService.isInGroup(dto.getGroupId(), userIds)) {
+            throw new GlobalException("部分用户不在群聊中");
+        }
         String key = buildWebrtcSessionKey(dto.getGroupId());
         if (redisTemplate.hasKey(key)) {
             throw new GlobalException("该群聊已存在一个通话");
         }
-        if (!groupMemberService.isInGroup(dto.getGroupId(), userIds)) {
-            throw new GlobalException("存在不在群聊中的用户");
-        }
         // 有效用户
         List<WebrtcUserInfo> userInfos = new LinkedList<>();
         // 离线用户
@@ -95,18 +100,22 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             WebrtcGroupFailedVO vo = new WebrtcGroupFailedVO();
             vo.setUserIds(offlineUserIds);
             vo.setReason("用户不在线");
-            sendMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), userInfo, JSON.toJSONString(vo));
+            sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), userInfo, JSON.toJSONString(vo));
         }
         if (!busyUserIds.isEmpty()) {
             WebrtcGroupFailedVO vo = new WebrtcGroupFailedVO();
             vo.setUserIds(busyUserIds);
             vo.setReason("用户正忙");
             IMUserInfo reciver = new IMUserInfo(userSession.getUserId(), userSession.getTerminal());
-            sendMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo));
+            sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo));
         }
         // 向被邀请的用户广播消息,发起呼叫
         List<Long> recvIds = getRecvIds(dto.getUserInfos());
-        sendMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), recvIds, JSON.toJSONString(userInfos));
+        sendRtcMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), recvIds, JSON.toJSONString(userInfos));
+        // 发送文字提示信息
+        WebrtcUserInfo mineInfo = findUserInfo(webrtcSession,userSession.getUserId());
+        String content = mineInfo.getNickName() + " 发起了语音通话";
+        sendTipMessage(dto.getGroupId(),content);
         log.info("发起群通话,userId:{},groupId:{}", userSession.getUserId(), dto.getGroupId());
     }
 
@@ -128,7 +137,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         saveWebrtcSession(groupId, webrtcSession);
         // 广播信令
         List<Long> recvIds = getRecvIds(webrtcSession.getUserInfos());
-        sendMessage1(MessageType.RTC_GROUP_ACCEPT, groupId, recvIds, "");
+        sendRtcMessage1(MessageType.RTC_GROUP_ACCEPT, groupId, recvIds, "");
         log.info("加入群通话,userId:{},groupId:{}", userSession.getUserId(), groupId);
     }
 
@@ -155,7 +164,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         userStateUtils.setFree(userSession.getUserId());
         // 广播消息给的所有用户
         List<Long> recvIds = getRecvIds(userInfos);
-        sendMessage1(MessageType.RTC_GROUP_REJECT, groupId, recvIds, "");
+        sendRtcMessage1(MessageType.RTC_GROUP_REJECT, groupId, recvIds, "");
         log.info("拒绝群通话,userId:{},groupId:{}", userSession.getUserId(), groupId);
     }
 
@@ -184,7 +193,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         vo.setUserIds(Arrays.asList(userSession.getUserId()));
         vo.setReason(dto.getReason());
         List<Long> recvIds = getRecvIds(userInfos);
-        sendMessage1(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), recvIds, JSON.toJSONString(vo));
+        sendRtcMessage1(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), recvIds, JSON.toJSONString(vo));
         log.info("群通话失败,userId:{},groupId:{},原因:{}", userSession.getUserId(), dto.getReason());
     }
 
@@ -193,7 +202,9 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
     public void join(Long groupId) {
         UserSession userSession = SessionContext.getSession();
         WebrtcGroupSession webrtcSession = getWebrtcSession(groupId);
-        // 校验
+        if (webrtcSession.getUserInfos().size() >= webrtcConfig.getMaxChannel()) {
+            throw new GlobalException("人员已满,无法进入通话");
+        }
         GroupMember member = groupMemberService.findByGroupAndUserId(groupId, userSession.getUserId());
         if (Objects.isNull(member) || member.getQuit()) {
             throw new GlobalException("您不在群里中");
@@ -217,7 +228,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         userStateUtils.setBusy(userSession.getUserId());
         // 广播信令
         List<Long> recvIds = getRecvIds(webrtcSession.getUserInfos());
-        sendMessage1(MessageType.RTC_GROUP_JOIN, groupId, recvIds, JSON.toJSONString(userInfo));
+        sendRtcMessage1(MessageType.RTC_GROUP_JOIN, groupId, recvIds, JSON.toJSONString(userInfo));
         log.info("加入群通话,userId:{},groupId:{}", userSession.getUserId(), groupId);
     }
 
@@ -226,6 +237,14 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
     public void invite(WebrtcGroupInviteDTO dto) {
         UserSession userSession = SessionContext.getSession();
         WebrtcGroupSession webrtcSession = getWebrtcSession(dto.getGroupId());
+        if (dto.getUserInfos().size() + dto.getUserInfos().size() > webrtcConfig.getMaxChannel()) {
+            throw new GlobalException("最多支持" + webrtcConfig.getMaxChannel() + "人进行通话");
+        }
+        if (!groupMemberService.isInGroup(dto.getGroupId(), getRecvIds(dto.getUserInfos()))) {
+            throw new GlobalException("部分用户不在群聊中");
+        }
+        // 保存开启通话提示消息
+
         // 过滤掉已经在通话中的用户
         List<WebrtcUserInfo> userInfos = webrtcSession.getUserInfos();
         // 原用户id
@@ -260,20 +279,20 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             vo.setUserIds(offlineUserIds);
             vo.setReason("用户不在线");
             IMUserInfo reciver = new IMUserInfo(userSession.getUserId(), userSession.getTerminal());
-            sendMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo));
+            sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo));
         }
         if (!busyUserIds.isEmpty()) {
             WebrtcGroupFailedVO vo = new WebrtcGroupFailedVO();
             vo.setUserIds(busyUserIds);
             vo.setReason("用户正在忙");
             IMUserInfo reciver = new IMUserInfo(userSession.getUserId(), userSession.getTerminal());
-            sendMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo));
+            sendRtcMessage2(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), reciver, JSON.toJSONString(vo));
         }
         // 向被邀请的发起呼叫
         List<Long> newUserIds = getRecvIds(newUserInfos);
-        sendMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), newUserIds, JSON.toJSONString(userInfos));
+        sendRtcMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), newUserIds, JSON.toJSONString(userInfos));
         // 向已在通话中的用户同步新邀请的用户信息
-        sendMessage1(MessageType.RTC_GROUP_INVITE, dto.getGroupId(), userIds, JSON.toJSONString(newUserInfos));
+        sendRtcMessage1(MessageType.RTC_GROUP_INVITE, dto.getGroupId(), userIds, JSON.toJSONString(newUserInfos));
         log.info("邀请加入群通话,userId:{},groupId:{},邀请用户:{}", userSession.getUserId(), dto.getGroupId(),
             newUserIds);
     }
@@ -293,7 +312,9 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         webrtcSession.getUserInfos().forEach(user -> userStateUtils.setFree(user.getId()));
         // 广播消息给的所有用户
         List<Long> recvIds = getRecvIds(webrtcSession.getUserInfos());
-        sendMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "");
+        sendRtcMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "");
+        // 发送文字提示信息
+        sendTipMessage(groupId,"通话结束");
         log.info("发起人取消群通话,userId:{},groupId:{}", userSession.getUserId(), groupId);
     }
 
@@ -319,7 +340,9 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             webrtcSession.getUserInfos().forEach(user -> userStateUtils.setFree(user.getId()));
             // 广播给还在呼叫中的用户,取消通话
             List<Long> recvIds = getRecvIds(webrtcSession.getUserInfos());
-            sendMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "");
+            sendRtcMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "");
+            // 发送文字提示信息
+            sendTipMessage(groupId,"通话结束");
             log.info("群通话结束,groupId:{}", groupId);
         } else {
             // 更新会话信息
@@ -330,7 +353,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             userStateUtils.setFree(userSession.getUserId());
             // 广播信令
             List<Long> recvIds = getRecvIds(userInfos);
-            sendMessage1(MessageType.RTC_GROUP_QUIT, groupId, recvIds, "");
+            sendRtcMessage1(MessageType.RTC_GROUP_QUIT, groupId, recvIds, "");
             log.info("用户退出群通话,userId:{},groupId:{}", userSession.getUserId(), groupId);
         }
     }
@@ -346,7 +369,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             return;
         }
         // 推送offer给对方
-        sendMessage2(MessageType.RTC_GROUP_OFFER, dto.getGroupId(), userInfo, dto.getOffer());
+        sendRtcMessage2(MessageType.RTC_GROUP_OFFER, dto.getGroupId(), userInfo, dto.getOffer());
         log.info("推送offer信息,userId:{},对方id:{},groupId:{}", userSession.getUserId(), dto.getUserId(),
             dto.getGroupId());
     }
@@ -363,7 +386,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             return;
         }
         // 推送answer信息给对方
-        sendMessage2(MessageType.RTC_GROUP_ANSWER, dto.getGroupId(), userInfo, dto.getAnswer());
+        sendRtcMessage2(MessageType.RTC_GROUP_ANSWER, dto.getGroupId(), userInfo, dto.getAnswer());
         log.info("回复answer信息,userId:{},对方id:{},groupId:{}", userSession.getUserId(), dto.getUserId(),
             dto.getGroupId());
     }
@@ -380,7 +403,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             return;
         }
         // 推送candidate信息给对方
-        sendMessage2(MessageType.RTC_GROUP_CANDIDATE, dto.getGroupId(), userInfo, dto.getCandidate());
+        sendRtcMessage2(MessageType.RTC_GROUP_CANDIDATE, dto.getGroupId(), userInfo, dto.getCandidate());
         log.info("同步candidate信息,userId:{},groupId:{}", userSession.getUserId(), dto.getGroupId());
     }
 
@@ -399,7 +422,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         saveWebrtcSession(dto.getGroupId(), webrtcSession);
         // 广播信令
         List<Long> recvIds = getRecvIds(webrtcSession.getUserInfos());
-        sendMessage1(MessageType.RTC_GROUP_DEVICE, dto.getGroupId(), recvIds, JSON.toJSONString(dto));
+        sendRtcMessage1(MessageType.RTC_GROUP_DEVICE, dto.getGroupId(), recvIds, JSON.toJSONString(dto));
         log.info("设备操作,userId:{},groupId:{},摄像头:{}", userSession.getUserId(), dto.getGroupId(),
             dto.getIsCamera());
     }
@@ -417,10 +440,10 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
             vo.setIsChating(true);
             vo.setUserInfos(webrtcSession.getUserInfos());
             Long hostId = webrtcSession.getHost().getId();
-            WebrtcUserInfo host = findUserInfo(webrtcSession,hostId);
+            WebrtcUserInfo host = findUserInfo(webrtcSession, hostId);
             if (Objects.isNull(host)) {
                 // 如果发起人已经退出了通话,则从数据库查询发起人数据
-                GroupMember member = groupMemberService.findByGroupAndUserId(groupId,hostId);
+                GroupMember member = groupMemberService.findByGroupAndUserId(groupId, hostId);
                 host = new WebrtcUserInfo();
                 host.setId(hostId);
                 host.setNickName(member.getAliasName());
@@ -437,11 +460,16 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         UserSession userSession = SessionContext.getSession();
         // 给通话session续命
         String key = buildWebrtcSessionKey(groupId);
-        redisTemplate.expire(key,30,TimeUnit.SECONDS);
+        redisTemplate.expire(key, 30, TimeUnit.SECONDS);
         // 用户忙线状态续命
         userStateUtils.expire(userSession.getUserId());
     }
 
+    @Override
+    public WebrtcConfig loadConfig() {
+        return webrtcConfig;
+    }
+
     private WebrtcGroupSession getWebrtcSession(Long groupId) {
         String key = buildWebrtcSessionKey(groupId);
         WebrtcGroupSession webrtcSession = (WebrtcGroupSession)redisTemplate.opsForValue().get(key);
@@ -492,7 +520,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         return webrtcSession.getUserInfos().stream().anyMatch(user -> user.getId().equals(userId));
     }
 
-    private void sendMessage1(MessageType messageType, Long groupId, List<Long> recvIds, String content) {
+    private void sendRtcMessage1(MessageType messageType, Long groupId, List<Long> recvIds, String content) {
         UserSession userSession = SessionContext.getSession();
         GroupMessageVO messageInfo = new GroupMessageVO();
         messageInfo.setType(messageType.code());
@@ -508,7 +536,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         imClient.sendGroupMessage(sendMessage);
     }
 
-    private void sendMessage2(MessageType messageType, Long groupId, IMUserInfo receiver, String content) {
+    private void sendRtcMessage2(MessageType messageType, Long groupId, IMUserInfo receiver, String content) {
         UserSession userSession = SessionContext.getSession();
         GroupMessageVO messageInfo = new GroupMessageVO();
         messageInfo.setType(messageType.code());
@@ -524,4 +552,28 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService {
         sendMessage.setData(messageInfo);
         imClient.sendGroupMessage(sendMessage);
     }
+
+    private void sendTipMessage(Long groupId,String content){
+        UserSession userSession = SessionContext.getSession();
+        // 群聊成员列表
+        List<Long> userIds = groupMemberService.findUserIdsByGroupId(groupId);
+        // 保存消息
+        GroupMessage msg = new GroupMessage();
+        msg.setGroupId(groupId);
+        msg.setContent(content);
+        msg.setSendId(userSession.getUserId());
+        msg.setSendTime(new Date());
+        msg.setStatus(MessageStatus.UNSEND.code());
+        msg.setSendNickName(userSession.getNickName());
+        msg.setType(MessageType.TIP_TEXT.code());
+        groupMessageService.save(msg);
+        // 群发罅隙
+        GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class);
+        IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
+        sendMessage.setSender(new IMUserInfo(userSession.getUserId(), userSession.getTerminal()));
+        sendMessage.setRecvIds(userIds);
+        sendMessage.setSendResult(false);
+        sendMessage.setData(msgInfo);
+        imClient.sendGroupMessage(sendMessage);
+    };
 }

+ 2 - 2
im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcPrivateServiceImpl.java

@@ -4,7 +4,7 @@ import com.bx.imclient.IMClient;
 import com.bx.imcommon.model.IMPrivateMessage;
 import com.bx.imcommon.model.IMUserInfo;
 import com.bx.implatform.config.ICEServer;
-import com.bx.implatform.config.ICEServerConfig;
+import com.bx.implatform.config.WebrtcConfig;
 import com.bx.implatform.contant.RedisKey;
 import com.bx.implatform.enums.MessageType;
 import com.bx.implatform.exception.GlobalException;
@@ -30,7 +30,7 @@ public class WebrtcPrivateServiceImpl implements IWebrtcPrivateService {
 
     private final IMClient imClient;
     private final RedisTemplate<String, Object> redisTemplate;
-    private final ICEServerConfig iceServerConfig;
+    private final WebrtcConfig iceServerConfig;
 
     @Override
     public void call(Long uid, String mode, String offer) {

+ 1 - 0
im-platform/src/main/resources/application.yml

@@ -39,6 +39,7 @@ minio:
   videoPath: video
 
 webrtc:
+  max-channel: 9 # 多人通话最大通道数量,最大不能超过16,建议值:4,9,16
   iceServers:
     - urls: stun:stun.l.google.com:19302