Browse Source

多终端同时在线后端改造(开发中)

master
xsx 3 years ago
parent
commit
0bb3f686e0
  1. 29
      im-client/src/main/java/com/bx/imclient/sender/IMSender.java
  2. 6
      im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java
  3. 22
      im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java
  4. 2
      im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java
  5. 2
      im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java
  6. 63
      im-platform/src/main/java/com/bx/implatform/controller/WebrtcController.java
  7. 32
      im-platform/src/main/java/com/bx/implatform/service/IWebrtcService.java
  8. 17
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  9. 252
      im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcServiceImpl.java
  10. 32
      im-platform/src/main/java/com/bx/implatform/session/WebrtcSession.java
  11. 2
      im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java
  12. 4
      im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java
  13. 4
      im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java
  14. 8
      im-ui/src/api/wssocket.js
  15. 159
      im-ui/src/components/chat/ChatPrivateVideo.vue
  16. 3
      im-ui/src/components/chat/ChatVideoAcceptor.vue
  17. 2
      im-ui/src/view/Home.vue

29
im-client/src/main/java/com/bx/imclient/sender/IMSender.java

@ -5,7 +5,6 @@ import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendCode; import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.*; import com.bx.imcommon.model.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -28,10 +27,11 @@ public class IMSender {
@Autowired @Autowired
private MessageListenerMulticaster listenerMulticaster; private MessageListenerMulticaster listenerMulticaster;
public void sendPrivateMessage(IMPrivateMessage message) { public void sendPrivateMessage(IMPrivateMessage<?> message) {
for (IMTerminalType terminal : IMTerminalType.values()) { List<Integer> terminals = message.getRecvTerminals();
for (Integer terminal : terminals) {
// 获取对方连接的channelId // 获取对方连接的channelId
String key = String.join(":",RedisKey.IM_USER_SERVER_ID, message.getRecvId().toString(), terminal.code().toString()); String key = String.join(":",RedisKey.IM_USER_SERVER_ID, message.getRecvId().toString(), terminal.toString());
Integer serverId = (Integer) redisTemplate.opsForValue().get(key); Integer serverId = (Integer) redisTemplate.opsForValue().get(key);
// 如果对方在线,将数据存储至redis,等待拉取推送 // 如果对方在线,将数据存储至redis,等待拉取推送
if (serverId != null) { if (serverId != null) {
@ -40,22 +40,21 @@ public class IMSender {
for (int i = 0; i < message.getDatas().size(); i++) { for (int i = 0; i < message.getDatas().size(); i++) {
IMRecvInfo recvInfo = new IMRecvInfo(); IMRecvInfo recvInfo = new IMRecvInfo();
recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code()); recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
recvInfo.setRecvTerminal(terminal.code()); recvInfo.setRecvTerminal(terminal);
recvInfo.setNeedSendResult(true); recvInfo.setSendResult(message.getSendResult());
List recvIds = new LinkedList(); List<Long> recvIds = new LinkedList<>();
recvIds.add(message.getRecvId()); recvIds.add(message.getRecvId());
recvInfo.setRecvIds(recvIds); recvInfo.setRecvIds(recvIds);
recvInfo.setData(message.getDatas().get(i)); recvInfo.setData(message.getDatas().get(i));
recvInfos[i]=recvInfo; recvInfos[i]=recvInfo;
} }
redisTemplate.opsForList().rightPushAll(sendKey, recvInfos); redisTemplate.opsForList().rightPushAll(sendKey, recvInfos);
} else if(message.getSendResult()){
} else {
// 回复消息状态 // 回复消息状态
for (int i = 0; i < message.getDatas().size(); i++) { for (int i = 0; i < message.getDatas().size(); i++) {
SendResult result = new SendResult(); SendResult result = new SendResult();
result.setRecvId(message.getRecvId()); result.setRecvId(message.getRecvId());
result.setRecvTerminal(terminal.code()); result.setRecvTerminal(terminal);
result.setCode(IMSendCode.NOT_ONLINE.code()); result.setCode(IMSendCode.NOT_ONLINE.code());
result.setData(message.getDatas().get(i)); result.setData(message.getDatas().get(i));
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result); listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result);
@ -63,9 +62,9 @@ public class IMSender {
} }
// 推送给自己的其他终端 // 推送给自己的其他终端
if (message.getSendToSelf() && !message.getSendTerminal().equals(terminal.code())) { if (message.getSendToSelf() && !message.getSendTerminal().equals(terminal)) {
// 获取终端连接的channelId // 获取终端连接的channelId
key = String.join(":",RedisKey.IM_USER_SERVER_ID, message.getSendId().toString(), terminal.code().toString()); key = String.join(":",RedisKey.IM_USER_SERVER_ID, message.getSendId().toString(), terminal.toString());
serverId = (Integer) redisTemplate.opsForValue().get(key); serverId = (Integer) redisTemplate.opsForValue().get(key);
// 如果终端在线,将数据存储至redis,等待拉取推送 // 如果终端在线,将数据存储至redis,等待拉取推送
if (serverId != null) { if (serverId != null) {
@ -74,10 +73,10 @@ public class IMSender {
for (int i = 0; i < message.getDatas().size(); i++) { for (int i = 0; i < message.getDatas().size(); i++) {
IMRecvInfo recvInfo = new IMRecvInfo(); IMRecvInfo recvInfo = new IMRecvInfo();
recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code()); recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
recvInfo.setRecvTerminal(terminal.code()); recvInfo.setRecvTerminal(terminal);
// 自己的消息不需要回推消息结果 // 自己的消息不需要回推消息结果
recvInfo.setNeedSendResult(false); recvInfo.setSendResult(false);
List recvIds = new LinkedList(); LinkedList<Long> recvIds = new LinkedList<>();
recvIds.add(message.getSendId()); recvIds.add(message.getSendId());
recvInfo.setRecvIds(recvIds); recvInfo.setRecvIds(recvIds);
recvInfo.setData(message.getDatas().get(i)); recvInfo.setData(message.getDatas().get(i));

6
im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java

@ -1,5 +1,8 @@
package com.bx.imcommon.enums; package com.bx.imcommon.enums;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public enum IMTerminalType { public enum IMTerminalType {
@ -24,6 +27,9 @@ public enum IMTerminalType {
return null; return null;
} }
public static List<Integer> codes(){
return Arrays.stream(values()).map(IMTerminalType::code).collect(Collectors.toList());
}
public String description() { public String description() {
return desc; return desc;

22
im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java

@ -3,7 +3,6 @@ package com.bx.imcommon.model;
import com.bx.imcommon.enums.IMTerminalType; import com.bx.imcommon.enums.IMTerminalType;
import lombok.Data; import lombok.Data;
import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -21,17 +20,28 @@ public class IMPrivateMessage<T> {
private Integer sendTerminal; private Integer sendTerminal;
/** /**
* 是否发送给自己的其他终端 * 接收者id
*/ */
private Boolean sendToSelf ; private Long recvId;
/** /**
* 接收者id * 接收者终端类型,默认全部
*/ */
private Long recvId; private List<Integer> recvTerminals = IMTerminalType.codes();
/**
* 是否发送给自己的其他终端,默认true
*/
private Boolean sendToSelf = true;
/**
* 是否需要回推发送结果,默认true
*/
private Boolean sendResult = true;
/** /**
* 消息内容(可一次性发送多条) * 消息内容
*/ */
private List<T> datas; private List<T> datas;

2
im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java

@ -30,7 +30,7 @@ public class IMRecvInfo {
/* /*
* 是否需要回调发送结果 * 是否需要回调发送结果
*/ */
private Boolean needSendResult = true; private Boolean sendResult;
/* /*
* 推送消息体 * 推送消息体

2
im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java

@ -4,6 +4,8 @@ public class RedisKey {
// 已读群聊消息位置(已读最大id) // 已读群聊消息位置(已读最大id)
public final static String IM_GROUP_READED_POSITION = "im:readed:group:position:"; public final static String IM_GROUP_READED_POSITION = "im:readed:group:position:";
// webrtc 会话信息
public final static String IM_WEBRTC_SESSION = "im:webrtc:session";
// 缓存前缀 // 缓存前缀
public final static String IM_CACHE = "im:cache:"; public final static String IM_CACHE = "im:cache:";
// 缓存是否好友:bool // 缓存是否好友:bool

63
im-platform/src/main/java/com/bx/implatform/controller/WebrtcController.java

@ -1,26 +1,15 @@
package com.bx.implatform.controller; package com.bx.implatform.controller;
import com.bx.implatform.config.ICEServer;
import cn.hutool.core.util.ArrayUtil;
import com.bx.imclient.IMClient;
import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.implatform.config.ICEServerConfig;
import com.bx.implatform.enums.MessageType;
import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.result.Result; import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils; import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.session.SessionContext; import com.bx.implatform.service.IWebrtcService;
import com.bx.implatform.session.UserSession;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.lang.reflect.Array; import java.util.List;
import java.util.Arrays;
@Api(tags = "webrtc视频单人通话") @Api(tags = "webrtc视频单人通话")
@RestController @RestController
@ -28,25 +17,19 @@ import java.util.Arrays;
public class WebrtcController { public class WebrtcController {
@Autowired @Autowired
private IMClient imClient; private IWebrtcService webrtcService;
@Autowired
private ICEServerConfig iceServerConfig;
@ApiOperation(httpMethod = "POST", value = "呼叫视频通话") @ApiOperation(httpMethod = "POST", value = "呼叫视频通话")
@PostMapping("/call") @PostMapping("/call")
public Result call(@RequestParam Long uid, @RequestBody String offer) { public Result call(@RequestParam Long uid, @RequestBody String offer) {
if(!imClient.isOnline(uid)){ webrtcService.call(uid,offer);
throw new GlobalException("对方目前不在线");
}
imClient.sendPrivateMessage(buildSendMessage(MessageType.RTC_CALL,uid,offer));
return ResultUtils.success(); return ResultUtils.success();
} }
@ApiOperation(httpMethod = "POST", value = "接受视频通话") @ApiOperation(httpMethod = "POST", value = "接受视频通话")
@PostMapping("/accept") @PostMapping("/accept")
public Result accept(@RequestParam Long uid,@RequestBody String answer) { public Result accept(@RequestParam Long uid,@RequestBody String answer) {
imClient.sendPrivateMessage(buildSendMessage(MessageType.RTC_ACCEPT,uid,answer)); webrtcService.accept(uid,answer);
return ResultUtils.success(); return ResultUtils.success();
} }
@ -54,59 +37,43 @@ public class WebrtcController {
@ApiOperation(httpMethod = "POST", value = "拒绝视频通话") @ApiOperation(httpMethod = "POST", value = "拒绝视频通话")
@PostMapping("/reject") @PostMapping("/reject")
public Result reject(@RequestParam Long uid) { public Result reject(@RequestParam Long uid) {
imClient.sendPrivateMessage(buildSendMessage(MessageType.RTC_REJECT,uid,null)); webrtcService.reject(uid);
return ResultUtils.success(); return ResultUtils.success();
} }
@ApiOperation(httpMethod = "POST", value = "取消呼叫") @ApiOperation(httpMethod = "POST", value = "取消呼叫")
@PostMapping("/cancel") @PostMapping("/cancel")
public Result cancel(@RequestParam Long uid) { public Result cancel(@RequestParam Long uid) {
imClient.sendPrivateMessage(buildSendMessage(MessageType.RTC_CANCEL,uid,null)); webrtcService.cancel(uid);
return ResultUtils.success(); return ResultUtils.success();
} }
@ApiOperation(httpMethod = "POST", value = "呼叫失败") @ApiOperation(httpMethod = "POST", value = "呼叫失败")
@PostMapping("/failed") @PostMapping("/failed")
public Result failed(@RequestParam Long uid,@RequestParam String reason) { public Result failed(@RequestParam Long uid,@RequestParam String reason) {
imClient.sendPrivateMessage(buildSendMessage(MessageType.RTC_FAILED,uid,reason)); webrtcService.failed(uid,reason);
return ResultUtils.success(); return ResultUtils.success();
} }
@ApiOperation(httpMethod = "POST", value = "挂断") @ApiOperation(httpMethod = "POST", value = "挂断")
@PostMapping("/handup") @PostMapping("/handup")
public Result leave(@RequestParam Long uid) { public Result leave(@RequestParam Long uid) {
imClient.sendPrivateMessage(buildSendMessage(MessageType.RTC_HANDUP,uid,null)); webrtcService.leave(uid);
return ResultUtils.success(); return ResultUtils.success();
} }
@PostMapping("/candidate") @PostMapping("/candidate")
@ApiOperation(httpMethod = "POST", value = "同步candidate") @ApiOperation(httpMethod = "POST", value = "同步candidate")
public Result candidate(@RequestParam Long uid,@RequestBody String candidate ) { public Result forwardCandidate(@RequestParam Long uid,@RequestBody String candidate ) {
imClient.sendPrivateMessage(buildSendMessage(MessageType.RTC_CANDIDATE,uid,candidate)); webrtcService.candidate(uid,candidate);
return ResultUtils.success(); return ResultUtils.success();
} }
@GetMapping("/iceservers") @GetMapping("/iceservers")
@ApiOperation(httpMethod = "GET", value = "获取iceservers") @ApiOperation(httpMethod = "GET", value = "获取iceservers")
public Result iceservers() { public Result<List<ICEServer>> iceservers() {
return ResultUtils.success(iceServerConfig.getIceServers()); return ResultUtils.success(webrtcService.getIceServers());
}
private IMPrivateMessage buildSendMessage(MessageType messageType,Long uid,String content){
UserSession session = SessionContext.getSession();
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(messageType.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
messageInfo.setContent(content);
IMPrivateMessage sendMessage = new IMPrivateMessage();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setSendToSelf(false);
sendMessage.setRecvId(uid);
sendMessage.setDatas(Arrays.asList(messageInfo));
return sendMessage;
} }
} }

32
im-platform/src/main/java/com/bx/implatform/service/IWebrtcService.java

@ -0,0 +1,32 @@
package com.bx.implatform.service;
import com.bx.implatform.config.ICEServer;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* webrtc 通信服务
* @author
*/
public interface IWebrtcService {
void call(Long uid, String offer);
void accept( Long uid,@RequestBody String answer);
void reject( Long uid);
void cancel( Long uid);
void failed( Long uid, String reason);
void leave( Long uid) ;
void candidate( Long uid, String candidate);
List<ICEServer> getIceServers();
}

17
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -19,13 +19,10 @@ import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession; import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils; import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.vo.PrivateMessageVO; import com.bx.implatform.vo.PrivateMessageVO;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -60,12 +57,12 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
this.save(msg); this.save(msg);
// 推送消息 // 推送消息
PrivateMessageInfo msgInfo = BeanUtils.copyProperties(msg, PrivateMessageInfo.class); PrivateMessageInfo msgInfo = BeanUtils.copyProperties(msg, PrivateMessageInfo.class);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage(); IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(msgInfo.getSendId()); sendMessage.setSendId(msgInfo.getSendId());
sendMessage.setRecvId(msgInfo.getRecvId()); sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendTerminal(session.getTerminal()); sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setSendToSelf(true); sendMessage.setSendToSelf(true);
sendMessage.setDatas(Arrays.asList(msgInfo)); sendMessage.setDatas(Collections.singletonList(msgInfo));
imClient.sendPrivateMessage(sendMessage); imClient.sendPrivateMessage(sendMessage);
log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), vo.getRecvId(), vo.getContent()); log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), vo.getRecvId(), vo.getContent());
return msg.getId(); return msg.getId();
@ -98,12 +95,12 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
msgInfo.setSendTime(new Date()); msgInfo.setSendTime(new Date());
msgInfo.setContent("对方撤回了一条消息"); msgInfo.setContent("对方撤回了一条消息");
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage(); IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(msgInfo.getSendId()); sendMessage.setSendId(msgInfo.getSendId());
sendMessage.setRecvId(msgInfo.getRecvId()); sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendTerminal(session.getTerminal()); sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setSendToSelf(true); sendMessage.setSendToSelf(true);
sendMessage.setDatas(Arrays.asList(msgInfo)); sendMessage.setDatas(Collections.singletonList(msgInfo));
imClient.sendPrivateMessage(sendMessage); imClient.sendPrivateMessage(sendMessage);
log.info("撤回私聊消息,发送id:{},接收id:{},内容:{}", msg.getSendId(), msg.getRecvId(), msg.getContent()); log.info("撤回私聊消息,发送id:{},接收id:{},内容:{}", msg.getSendId(), msg.getRecvId(), msg.getContent());
} }
@ -122,7 +119,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
page = page > 0 ? page : 1; page = page > 0 ? page : 1;
size = size > 0 ? size : 10; size = size > 0 ? size : 10;
Long userId = SessionContext.getSession().getUserId(); Long userId = SessionContext.getSession().getUserId();
Long stIdx = (page - 1) * size; long stIdx = (page - 1) * size;
QueryWrapper<PrivateMessage> wrapper = new QueryWrapper<>(); QueryWrapper<PrivateMessage> wrapper = new QueryWrapper<>();
wrapper.lambda().and(wrap -> wrap.and( wrapper.lambda().and(wrap -> wrap.and(
wp -> wp.eq(PrivateMessage::getSendId, userId) wp -> wp.eq(PrivateMessage::getSendId, userId)
@ -159,7 +156,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
if (!messages.isEmpty()) { if (!messages.isEmpty()) {
List<PrivateMessageInfo> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageInfo.class)).collect(Collectors.toList()); List<PrivateMessageInfo> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageInfo.class)).collect(Collectors.toList());
// 推送消息 // 推送消息
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage(); IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setRecvId(userId); sendMessage.setRecvId(userId);
sendMessage.setSendToSelf(false); sendMessage.setSendToSelf(false);
sendMessage.setDatas(messageInfos); sendMessage.setDatas(messageInfos);

252
im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcServiceImpl.java

@ -0,0 +1,252 @@
package com.bx.implatform.service.impl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.implatform.config.ICEServer;
import com.bx.implatform.config.ICEServerConfig;
import com.bx.implatform.contant.RedisKey;
import com.bx.implatform.enums.MessageType;
import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.service.IWebrtcService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.session.WebrtcSession;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class WebrtcServiceImpl implements IWebrtcService {
@Autowired
private IMClient imClient;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ICEServerConfig iceServerConfig;
@Override
public void call(Long uid, String offer) {
UserSession session = SessionContext.getSession();
if (!imClient.isOnline(uid)) {
throw new GlobalException("对方目前不在线");
}
// 创建webrtc会话
WebrtcSession webrtcSession = new WebrtcSession();
webrtcSession.setCallerId(session.getUserId());
webrtcSession.setCallerTerminal(session.getTerminal());
String key = getSessionKey(session.getUserId(), uid);
redisTemplate.opsForValue().set(key, webrtcSession, 12, TimeUnit.HOURS);
// 向对方所有终端发起呼叫
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(MessageType.RTC_CALL.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
messageInfo.setContent(offer);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
sendMessage.setDatas(Collections.singletonList(messageInfo));
imClient.sendPrivateMessage(sendMessage);
}
@Override
public void accept(Long uid, @RequestBody String answer) {
UserSession session = SessionContext.getSession();
// 查询webrtc会话
WebrtcSession webrtcSession = getWebrtcSession(session.getUserId(), uid);
// 更新接受者信息
webrtcSession.setAcceptorId(session.getUserId());
webrtcSession.setAcceptorTerminal(session.getTerminal());
String key = getSessionKey(session.getUserId(), uid);
redisTemplate.opsForValue().set(key, webrtcSession, 12, TimeUnit.HOURS);
// 向发起人推送接受通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(MessageType.RTC_ACCEPT.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
messageInfo.setContent(answer);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setRecvId(uid);
// 告知其他终端已经接受会话,中止呼叫
sendMessage.setSendToSelf(true);
sendMessage.setSendResult(false);
sendMessage.setRecvTerminals((Collections.singletonList(webrtcSession.getCallerTerminal())));
sendMessage.setDatas(Collections.singletonList(messageInfo));
imClient.sendPrivateMessage(sendMessage);
}
@Override
public void reject(Long uid) {
UserSession session = SessionContext.getSession();
// 查询webrtc会话
WebrtcSession webrtcSession = getWebrtcSession(session.getUserId(), uid);
// 删除会话信息
removeWebrtcSession(uid, session.getUserId());
// 向发起人推送拒绝通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(MessageType.RTC_REJECT.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setRecvId(uid);
// 告知其他终端已经拒绝会话,中止呼叫
sendMessage.setSendToSelf(true);
sendMessage.setSendResult(false);
sendMessage.setRecvTerminals(Collections.singletonList(webrtcSession.getCallerTerminal()));
sendMessage.setDatas(Collections.singletonList(messageInfo));
imClient.sendPrivateMessage(sendMessage);
}
@Override
public void cancel(Long uid) {
UserSession session = SessionContext.getSession();
// 删除会话信息
removeWebrtcSession(session.getUserId(), uid);
// 向对方所有终端推送取消通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(MessageType.RTC_ACCEPT.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
sendMessage.setDatas(Collections.singletonList(messageInfo));
// 通知对方取消会话
imClient.sendPrivateMessage(sendMessage);
}
@Override
public void failed(Long uid, String reason) {
UserSession session = SessionContext.getSession();
// 查询webrtc会话
WebrtcSession webrtcSession = getWebrtcSession(session.getUserId(), uid);
// 删除会话信息
removeWebrtcSession(uid, session.getUserId());
// 向发起方推送通话失败信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(MessageType.RTC_FAILED.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setRecvId(uid);
// 告知其他终端已经会话失败,中止呼叫
sendMessage.setSendToSelf(true);
sendMessage.setSendResult(false);
sendMessage.setRecvTerminals(Collections.singletonList(webrtcSession.getCallerTerminal()));
sendMessage.setDatas(Collections.singletonList(messageInfo));
// 通知对方取消会话
imClient.sendPrivateMessage(sendMessage);
}
@Override
public void leave(Long uid) {
UserSession session = SessionContext.getSession();
// 查询webrtc会话
WebrtcSession webrtcSession = getWebrtcSession(session.getUserId(), uid);
// 删除会话信息
removeWebrtcSession(uid, session.getUserId());
// 向对方推送挂断通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(MessageType.RTC_HANDUP.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
Integer terminal = getTerminalType(uid, webrtcSession);
sendMessage.setRecvTerminals(Collections.singletonList(terminal));
sendMessage.setDatas(Collections.singletonList(messageInfo));
// 通知对方取消会话
imClient.sendPrivateMessage(sendMessage);
}
@Override
public void candidate(Long uid, String candidate) {
UserSession session = SessionContext.getSession();
// 查询webrtc会话
WebrtcSession webrtcSession = getWebrtcSession(session.getUserId(), uid);
// 向发起方推送同步candidate信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
messageInfo.setType(MessageType.RTC_CANDIDATE.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
messageInfo.setContent(candidate);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
sendMessage.setSendId(session.getUserId());
sendMessage.setSendTerminal(session.getTerminal());
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
Integer terminal = getTerminalType(uid, webrtcSession);
sendMessage.setRecvTerminals(Collections.singletonList(terminal));
sendMessage.setDatas(Collections.singletonList(messageInfo));
imClient.sendPrivateMessage(sendMessage);
}
@Override
public List<ICEServer> getIceServers() {
return iceServerConfig.getIceServers();
}
private WebrtcSession getWebrtcSession(Long userId, Long uid) {
String key = getSessionKey(userId, uid);
WebrtcSession webrtcSession = (WebrtcSession)redisTemplate.opsForValue().get(key);
if (webrtcSession == null) {
throw new GlobalException("视频通话已结束");
}
return webrtcSession;
}
private void removeWebrtcSession(Long userId, Long uid) {
String key = getSessionKey(userId, uid);
redisTemplate.delete(key);
}
private String getSessionKey(Long id1, Long id2) {
Long minId = id1 > id2 ? id2 : id1;
Long maxId = id1 > id2 ? id1 : id2;
return String.join(":", RedisKey.IM_WEBRTC_SESSION, minId.toString(), maxId.toString());
}
private Integer getTerminalType(Long uid, WebrtcSession webrtcSession) {
if (uid.equals(webrtcSession.getCallerId())) {
return webrtcSession.getCallerTerminal();
}
return webrtcSession.getAcceptorTerminal();
}
}

32
im-platform/src/main/java/com/bx/implatform/session/WebrtcSession.java

@ -0,0 +1,32 @@
package com.bx.implatform.session;
import com.bx.imcommon.enums.IMTerminalType;
import io.swagger.models.auth.In;
import lombok.Data;
/*
* webrtc 会话信息
* @Author Blue
* @Date 2022/10/21
*/
@Data
public class WebrtcSession {
/**
* 发起者id
*/
private Long callerId;
/**
* 发起者终端类型
*/
private Integer callerTerminal;
/**
* 接受者id
*/
private Long acceptorId;
/**
* 接受者终端类型
*/
private Integer acceptorTerminal;
}

2
im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java

@ -48,7 +48,7 @@ public class PrivateMessageProcessor extends AbstractMessageProcessor<IMRecvInfo
} }
private void sendResult(IMRecvInfo recvInfo,IMSendCode sendCode){ private void sendResult(IMRecvInfo recvInfo,IMSendCode sendCode){
if(recvInfo.getNeedSendResult()) { if(recvInfo.getSendResult()) {
String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; String key = RedisKey.IM_RESULT_PRIVATE_QUEUE;
SendResult result = new SendResult(); SendResult result = new SendResult();
result.setRecvId(recvInfo.getRecvIds().get(0)); result.setRecvId(recvInfo.getRecvIds().get(0));

4
im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java

@ -24,8 +24,8 @@ public class PullUnreadGroupMessageTask extends AbstractPullMessageTask {
public void pullMessage() { public void pullMessage() {
// 从redis拉取未读消息 // 从redis拉取未读消息
String key = RedisKey.IM_UNREAD_GROUP_QUEUE + IMServerGroup.serverId; String key = RedisKey.IM_UNREAD_GROUP_QUEUE + IMServerGroup.serverId;
List messageInfos = redisTemplate.opsForList().range(key,0,-1); List recvInfos = redisTemplate.opsForList().range(key,0,-1);
for(Object o: messageInfos){ for(Object o: recvInfos){
redisTemplate.opsForList().leftPop(key); redisTemplate.opsForList().leftPop(key);
IMRecvInfo recvInfo = (IMRecvInfo)o; IMRecvInfo recvInfo = (IMRecvInfo)o;
AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE); AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE);

4
im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java

@ -27,8 +27,8 @@ public class PullUnreadPrivateMessageTask extends AbstractPullMessageTask {
public void pullMessage() { public void pullMessage() {
// 从redis拉取未读消息 // 从redis拉取未读消息
String key = RedisKey.IM_UNREAD_PRIVATE_QUEUE + IMServerGroup.serverId; String key = RedisKey.IM_UNREAD_PRIVATE_QUEUE + IMServerGroup.serverId;
List messageInfos = redisTemplate.opsForList().range(key,0,-1); List recvInfos = redisTemplate.opsForList().range(key,0,-1);
for(Object o: messageInfos){ for(Object o: recvInfos){
redisTemplate.opsForList().leftPop(key); redisTemplate.opsForList().leftPop(key);
IMRecvInfo recvInfo = (IMRecvInfo)o; IMRecvInfo recvInfo = (IMRecvInfo)o;
AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE); AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE);

8
im-ui/src/api/wssocket.js

@ -18,8 +18,9 @@ let createWebSocket = (url, id,token) => {
let initWebSocket = () => { let initWebSocket = () => {
try { try {
console.log("初始化WebSocket"); console.log("初始化WebSocket");
closeWebSocket();
hasLogin = false; hasLogin = false;
websock = new WebSocket(wsurl); websock = new WebSocket(wsurl);
websock.onmessage = function(e) { websock.onmessage = function(e) {
let sendInfo = JSON.parse(e.data) let sendInfo = JSON.parse(e.data)
if (sendInfo.cmd == 0) { if (sendInfo.cmd == 0) {
@ -39,6 +40,7 @@ let initWebSocket = () => {
websock.onclose = function(e) { websock.onclose = function(e) {
console.log('WebSocket连接关闭') console.log('WebSocket连接关闭')
isConnect = false; //断开后修改标识 isConnect = false; //断开后修改标识
reConnect();
} }
websock.onopen = function() { websock.onopen = function() {
console.log("WebSocket连接成功"); console.log("WebSocket连接成功");
@ -77,7 +79,7 @@ let reConnect = () => {
}; };
//设置关闭连接 //设置关闭连接
let closeWebSocket = () => { let closeWebSocket = () => {
websock.close(); websock && websock.close();
}; };
@ -147,4 +149,4 @@ export {
sendMessage, sendMessage,
onmessage, onmessage,
onopen onopen
} }

159
im-ui/src/components/chat/ChatPrivateVideo.vue

@ -1,24 +1,23 @@
<template> <template>
<el-dialog v-dialogDrag :title="title" top="5vh" <el-dialog v-dialogDrag :title="title" top="5vh" :close-on-click-modal="false" :close-on-press-escape="false"
:close-on-click-modal="false" :visible.sync="visible" width="50%" height="70%" :before-close="handleClose">
:close-on-press-escape="false"
:visible.sync="visible" width="50%" height="70%" :before-close="handleClose">
<div class="chat-video"> <div class="chat-video">
<div class="chat-video-box"> <div class="chat-video-box">
<div class="chat-video-friend" v-loading="loading" element-loading-text="等待对方接听..." element-loading-spinner="el-icon-loading" <div class="chat-video-friend" v-loading="loading" element-loading-text="等待对方接听..."
element-loading-background="rgba(0, 0, 0, 0.5)"> element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.5)">
<head-image class="friend-head-image" :id="this.friend.id" :size="80" :url="this.friend.headImage"></head-image> <head-image class="friend-head-image" :id="this.friend.id" :size="80"
:url="this.friend.headImage"></head-image>
<video ref="friendVideo" autoplay=""></video> <video ref="friendVideo" autoplay=""></video>
</div> </div>
<div class="chat-video-mine"> <div class="chat-video-mine">
<video ref="mineVideo" autoplay=""></video> <video ref="mineVideo" autoplay=""></video>
</div> </div>
</div> </div>
<div class="chat-video-controllbar"> <div class="chat-video-controllbar">
<div v-show="state=='CONNECTING'" title="取消呼叫" class="icon iconfont icon-phone-reject reject" style="color: red;" <div v-show="state=='CONNECTING'" title="取消呼叫" class="icon iconfont icon-phone-reject reject"
@click="cancel()"></div> style="color: red;" @click="cancel()"></div>
<div v-show="state=='CONNECTED'" title="挂断" class="icon iconfont icon-phone-reject reject" style="color: red;" <div v-show="state=='CONNECTED'" title="挂断" class="icon iconfont icon-phone-reject reject"
@click="handup()"></div> style="color: red;" @click="handup()"></div>
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
@ -27,10 +26,12 @@
<script> <script>
import HeadImage from '../common/HeadImage.vue'; import HeadImage from '../common/HeadImage.vue';
export default { export default {
name: 'chatVideo', name: 'chatVideo',
components: {HeadImage}, components: {
HeadImage
},
props: { props: {
visible: { visible: {
type: Boolean type: Boolean
@ -47,6 +48,7 @@
}, },
data() { data() {
return { return {
callerId: null,
stream: null, stream: null,
audio: new Audio(), audio: new Audio(),
loading: false, loading: false,
@ -133,7 +135,7 @@
this.peerConnection.oniceconnectionstatechange = (event) => { this.peerConnection.oniceconnectionstatechange = (event) => {
let state = event.target.iceConnectionState; let state = event.target.iceConnectionState;
console.log("ICE connection status changed : " + state) console.log("ICE connection status changed : " + state)
if(state == 'connected'){ if (state == 'connected') {
this.resetTime(); this.resetTime();
} }
}; };
@ -141,30 +143,42 @@
}, },
handleMessage(msg) { handleMessage(msg) {
if (msg.type == this.$enums.MESSAGE_TYPE.RTC_ACCEPT) { if (msg.type == this.$enums.MESSAGE_TYPE.RTC_ACCEPT) {
this.peerConnection.setRemoteDescription(new RTCSessionDescription(JSON.parse(msg.content))); if (msg.sendId == this.$store.state.userStore.userInfo.id) {
// //
this.loading = false; this.$message.success("已在其他设备接听");
// this.close();
this.state = 'CONNECTED'; } else {
// //
this.audio.pause(); this.peerConnection.setRemoteDescription(new RTCSessionDescription(JSON.parse(msg.content)));
// candidate //
this.candidates.forEach((candidate) => { this.loading = false;
this.sendCandidate(candidate); //
}) this.state = 'CONNECTED';
} //
else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_REJECT) { this.audio.pause();
this.$message.error("对方拒绝了您的视频请求"); // candidate
this.close(); this.candidates.forEach((candidate) => {
} this.sendCandidate(candidate);
else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_FAILED) { })
}
} else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_REJECT) {
console.log(msg)
if (msg.sendId == this.$store.state.userStore.userInfo.id) {
//
this.$message.success("已在其他设备拒绝通话");
this.close();
} else {
//
this.$message.error("对方拒绝了您的视频请求");
this.close();
}
} else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_FAILED) {
this.$message.error(msg.content) this.$message.error(msg.content)
this.close(); this.close();
} } else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CANDIDATE) {
else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CANDIDATE) {
this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(msg.content))); this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(msg.content)));
} } else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_HANDUP) {
else if (msg.type == this.$enums.MESSAGE_TYPE.RTC_HANDUP) {
this.$message.success("对方挂断了视频通话"); this.$message.success("对方挂断了视频通话");
this.close(); this.close();
} }
@ -177,6 +191,7 @@
method: 'post', method: 'post',
data: JSON.stringify(offer) data: JSON.stringify(offer)
}).then(() => { }).then(() => {
this.callId = this.$store.state.userStore.userInfo.id;
this.loading = true; this.loading = true;
this.state = 'CONNECTING'; this.state = 'CONNECTING';
this.audio.play(); this.audio.play();
@ -195,8 +210,11 @@
url: `/webrtc/private/accept?uid=${this.friend.id}`, url: `/webrtc/private/accept?uid=${this.friend.id}`,
method: 'post', method: 'post',
data: JSON.stringify(answer) data: JSON.stringify(answer)
}).then(() => {
this.state = 'CONNECTED';
this.callerId = this.friend.id;
}) })
this.state = 'CONNECTED';
}, },
(error) => { (error) => {
this.$message.error(error); this.$message.error(error);
@ -242,17 +260,22 @@
this.audio.pause(); this.audio.pause();
this.candidates = []; this.candidates = [];
this.$store.commit("setUserState", this.$enums.USER_STATE.FREE); this.$store.commit("setUserState", this.$enums.USER_STATE.FREE);
this.$refs.friendVideo.srcObject = null; if (this.peerConnection) {
this.peerConnection.close(); this.peerConnection.close();
this.peerConnection.onicecandidate = null; this.peerConnection.onicecandidate = null;
this.peerConnection.onaddstream = null; this.peerConnection.onaddstream = null;
}
if (this.$refs.friendVideo) {
this.$refs.friendVideo.srcObject = null;
}
}, },
resetTime(){ resetTime() {
this.videoTime = 0; this.videoTime = 0;
this.videoTimer && clearInterval(this.videoTimer); this.videoTimer && clearInterval(this.videoTimer);
this.videoTimer = setInterval(()=>{ this.videoTimer = setInterval(() => {
this.videoTime++; this.videoTime++;
},1000) }, 1000)
}, },
handleClose() { handleClose() {
if (this.state == 'CONNECTED') { if (this.state == 'CONNECTED') {
@ -264,22 +287,26 @@
} }
}, },
hasUserMedia() { hasUserMedia() {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator
.mozGetUserMedia ||
navigator.msGetUserMedia; navigator.msGetUserMedia;
return !!navigator.getUserMedia; return !!navigator.getUserMedia;
}, },
hasRTCPeerConnection() { hasRTCPeerConnection() {
window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window
window.RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription; .mozRTCPeerConnection;
window.RTCIceCandidate = window.RTCIceCandidate || window.webkitRTCIceCandidate || window.mozRTCIceCandidate; window.RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window
.mozRTCSessionDescription;
window.RTCIceCandidate = window.RTCIceCandidate || window.webkitRTCIceCandidate || window
.mozRTCIceCandidate;
return !!window.RTCPeerConnection; return !!window.RTCPeerConnection;
}, },
initAudio(){ initAudio() {
let url = require(`@/assets/audio/call.wav`); let url = require(`@/assets/audio/call.wav`);
this.audio.src = url; this.audio.src = url;
this.audio.loop = true; this.audio.loop = true;
}, },
initICEServers(){ initICEServers() {
this.$http({ this.$http({
url: '/webrtc/private/iceservers', url: '/webrtc/private/iceservers',
method: 'get' method: 'get'
@ -287,7 +314,7 @@
this.configuration.iceServers = servers; this.configuration.iceServers = servers;
}) })
} }
}, },
watch: { watch: {
visible: { visible: {
@ -302,25 +329,25 @@
}, },
computed: { computed: {
title() { title() {
let strTitle = `视频聊天-${this.friend.nickName}`; let strTitle = `视频聊天-${this.friend.nickName}`;
if(this.state == 'CONNECTED'){ if (this.state == 'CONNECTED') {
strTitle += `(${this.currentTime})`; strTitle += `(${this.currentTime})`;
}else if(this.state == 'CONNECTING'){ } else if (this.state == 'CONNECTING') {
strTitle += `(呼叫中)`; strTitle += `(呼叫中)`;
} }
return strTitle; return strTitle;
}, },
currentTime(){ currentTime() {
let currentTime = 0; let currentTime = 0;
if(this.state == 'CONNECTED' && this.videoTime){ if (this.state == 'CONNECTED' && this.videoTime) {
currentTime = Math.floor(this.videoTime); currentTime = Math.floor(this.videoTime);
} }
let min = Math.floor(currentTime/60); let min = Math.floor(currentTime / 60);
let sec = currentTime%60; let sec = currentTime % 60;
let strTime = min<10?"0":""; let strTime = min < 10 ? "0" : "";
strTime += min; strTime += min;
strTime += ":" strTime += ":"
strTime += sec<10?"0":""; strTime += sec < 10 ? "0" : "";
strTime += sec; strTime += sec;
return strTime; return strTime;
} }
@ -335,14 +362,15 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.chat-video { .chat-video {
position: relative; position: relative;
.chat-video-box { .chat-video-box {
position: relative; position: relative;
border: #4880b9 solid 1px; border: #4880b9 solid 1px;
background-color: #eeeeee; background-color: #eeeeee;
.chat-video-friend { .chat-video-friend {
height: 70vh; height: 70vh;
.friend-head-image { .friend-head-image {
position: absolute; position: absolute;
} }
@ -360,8 +388,9 @@
width: 25vh; width: 25vh;
right: 0; right: 0;
bottom: 0; bottom: 0;
box-shadow: 0px 0px 5px #ccc; box-shadow: 0px 0px 5px #ccc;
background-color: #cccccc; background-color: #cccccc;
video { video {
width: 100%; width: 100%;
} }
@ -379,4 +408,4 @@
} }
} }
} }
</style> </style>

3
im-ui/src/components/chat/ChatVideoAcceptor.vue

@ -69,8 +69,11 @@
this.audio.play(); this.audio.play();
}, },
onCancel(){ onCancel(){
//
this.$message.success("对方取消了呼叫"); this.$message.success("对方取消了呼叫");
this.close(); this.close();
}, },
handleMessage(msgInfo){ handleMessage(msgInfo){
if(msgInfo.type == this.$enums.MESSAGE_TYPE.RTC_CALL){ if(msgInfo.type == this.$enums.MESSAGE_TYPE.RTC_CALL){

2
im-ui/src/view/Home.vue

@ -136,12 +136,14 @@
// webrtc // webrtc
if (msg.type >= this.$enums.MESSAGE_TYPE.RTC_CALL && if (msg.type >= this.$enums.MESSAGE_TYPE.RTC_CALL &&
msg.type <= this.$enums.MESSAGE_TYPE.RTC_CANDIDATE) { msg.type <= this.$enums.MESSAGE_TYPE.RTC_CANDIDATE) {
// //
if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL || if (msg.type == this.$enums.MESSAGE_TYPE.RTC_CALL ||
msg.type == this.$enums.MESSAGE_TYPE.RTC_CANCEL) { msg.type == this.$enums.MESSAGE_TYPE.RTC_CANCEL) {
this.$store.commit("showVideoAcceptorBox", friend); this.$store.commit("showVideoAcceptorBox", friend);
this.$refs.videoAcceptor.handleMessage(msg) this.$refs.videoAcceptor.handleMessage(msg)
} else { } else {
this.$refs.videoAcceptor.close()
this.$refs.privateVideo.handleMessage(msg) this.$refs.privateVideo.handleMessage(msg)
} }
return; return;

Loading…
Cancel
Save