这是kurento tutorial中的一个例子(groupCall),用于多人音视频通话,效果如下:

登录界面:

聊天界面:

运行方法:

1、本地用docker把kurento server跑起来

2、idea里启用这个项目

3、浏览器里输入https://localhost:8443/ 输入用户名、房间号,然后再开一个浏览器tab页,输入一个不同的用户名,房间号与第1个tab相同,正常情况下,这2个tab页就能聊上了,还可以再加更多tab模拟多人视频(注:docker容器性能有限,mac本上实测,越过4个人,就很不稳定了)

下面是该项目的一些代码和逻辑分析:

一、主要模型的类图如下:

UserSession类:代表每个连接进来的用户会话信息。

Room类:即房间,1个房间可能有多个UserSession实例。

RoomManager类:房间管理,用于创建或销毁房间。

UserRegistry类:用户注册类,即管理用户。

二、主要代码逻辑:

1、创建房间入口

  public Room getRoom(String roomName) {log.debug("Searching for room {}", roomName);Room room = rooms.get(roomName);if (room == null) {log.debug("Room {} not existent. Will create now!", roomName);room = new Room(roomName, kurento.createMediaPipeline());rooms.put(roomName, room);}log.debug("Room {} found!", roomName);return room;}

注:第7行,每个房间实例创建时,都绑定了一个对应的MediaPipeline(用于隔离不同房间的媒体信息等)

2、创建用户实例入口

    public UserSession(final String name, String roomName, final WebSocketSession session,MediaPipeline pipeline) {this.pipeline = pipeline;this.name = name;this.session = session;this.roomName = roomName;this.outgoingMedia = new WebRtcEndpoint.Builder(pipeline).build();this.outgoingMedia.addIceCandidateFoundListener(event -> {JsonObject response = new JsonObject();response.addProperty("id", "iceCandidate");response.addProperty("name", name);response.add("candidate", JsonUtils.toJsonObject(event.getCandidate()));try {synchronized (session) {session.sendMessage(new TextMessage(response.toString()));}} catch (IOException e) {log.debug(e.getMessage());}});}

UserSession的构造函数上,把房间实例的pipeline做为入参传进来,然后上行传输的WebRtcEndPoint实例outgoingMedia又跟pipeline绑定(第8行)。这样:"用户实例--pipeline实例--房间实例" 就串起来了。

用户加入房间的代码:

    public UserSession join(String userName, WebSocketSession session) throws IOException {log.info("ROOM {}: adding participant {}", this.name, userName);final UserSession participant = new UserSession(userName, this.name, session, this.pipeline);//示例工程上,没考虑“相同用户名”的人进入同1个房间的情况,这里加上了“用户名重名”检测if (participants.containsKey(userName)) {final JsonObject jsonFailMsg = new JsonObject();final JsonArray jsonFailArray = new JsonArray();jsonFailArray.add(userName + " exist!");jsonFailMsg.addProperty("id", "joinFail");jsonFailMsg.add("data", jsonFailArray);participant.sendMessage(jsonFailMsg);participant.close();return null;}joinRoom(participant);participants.put(participant.getName(), participant);sendParticipantNames(participant);return participant;}

原代码没考虑到用户名重名的问题,我加上了这段检测,倒数第2行代码,sendParticipantNames在加入成功后,给房间里的其它人发通知。

3、SDP交换的入口

kurento-group-call/src/main/resources/static/js/conferenceroom.js 中有一段监听websocket的代码:

ws.onmessage = function (message) {let parsedMessage = JSON.parse(message.data);console.info('Received message: ' + message.data);switch (parsedMessage.id) {case 'existingParticipants':onExistingParticipants(parsedMessage);break;case 'newParticipantArrived':onNewParticipant(parsedMessage);break;case 'participantLeft':onParticipantLeft(parsedMessage);break;case 'receiveVideoAnswer':receiveVideoResponse(parsedMessage);break;case 'iceCandidate':participants[parsedMessage.name].rtcPeer.addIceCandidate(parsedMessage.candidate, function (error) {if (error) {console.error("Error adding candidate: " + error);return;}});break;case 'joinFail':alert(parsedMessage.data[0]);window.location.reload();break;default:console.error('Unrecognized message', parsedMessage);}
}

服务端在刚才提到的sendParticipantNames后,会给js发送各种消息,existingParticipants(其它人加入)、newParticipantArrived(新人加入) 这二类消息,就会触发generateOffer,开始向服务端发送SDP

function onExistingParticipants(msg) {const constraints = {audio: true,video: {mandatory: {maxWidth: 320,maxFrameRate: 15,minFrameRate: 15}}};console.log(name + " registered in room " + room);let participant = new Participant(name);participants[name] = participant;let video = participant.getVideoElement();const options = {localVideo: video,mediaConstraints: constraints,onicecandidate: participant.onIceCandidate.bind(participant)};participant.rtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options,function (error) {if (error) {return console.error(error);}this.generateOffer(participant.offerToReceiveVideo.bind(participant));});msg.data.forEach(receiveVideo);
}

4、服务端回应各种websocket消息

org.kurento.tutorial.groupcall.CallHandler#handleTextMessage 信令处理的主要逻辑,就在这里:

    @Overridepublic void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {final JsonObject jsonMessage = gson.fromJson(message.getPayload(), JsonObject.class);final UserSession user = registry.getBySession(session);if (user != null) {log.debug("Incoming message from user '{}': {}", user.getName(), jsonMessage);} else {log.debug("Incoming message from new user: {}", jsonMessage);}switch (jsonMessage.get("id").getAsString()) {case "joinRoom":joinRoom(jsonMessage, session);break;case "receiveVideoFrom":final String senderName = jsonMessage.get("sender").getAsString();final UserSession sender = registry.getByName(senderName);final String sdpOffer = jsonMessage.get("sdpOffer").getAsString();user.receiveVideoFrom(sender, sdpOffer);break;case "leaveRoom":leaveRoom(user);break;case "onIceCandidate":JsonObject candidate = jsonMessage.get("candidate").getAsJsonObject();if (user != null) {IceCandidate cand = new IceCandidate(candidate.get("candidate").getAsString(),candidate.get("sdpMid").getAsString(), candidate.get("sdpMLineIndex").getAsInt());user.addCandidate(cand, jsonMessage.get("name").getAsString());}break;default:break;}}

其中user.receiveVideoFrom方法,就会回应SDP

    public void receiveVideoFrom(UserSession sender, String sdpOffer) throws IOException {log.info("USER {}: connecting with {} in room {}", this.name, sender.getName(), this.roomName);log.trace("USER {}: SdpOffer for {} is {}", this.name, sender.getName(), sdpOffer);final String ipSdpAnswer = this.getEndpointForUser(sender).processOffer(sdpOffer);final JsonObject scParams = new JsonObject();scParams.addProperty("id", "receiveVideoAnswer");scParams.addProperty("name", sender.getName());scParams.addProperty("sdpAnswer", ipSdpAnswer);log.trace("USER {}: SdpAnswer for {} is {}", this.name, sender.getName(), ipSdpAnswer);this.sendMessage(scParams);log.debug("gather candidates");this.getEndpointForUser(sender).gatherCandidates();}

SDP和ICE信息交换完成,就开始视频通讯了。

参考文章:

https://doc-kurento.readthedocs.io/en/6.10.0/tutorials/java/tutorial-groupcall.html

转载于:https://www.cnblogs.com/yjmyzz/p/webrtc-groupcall-using-kurento.html

webrtc笔记(5): 基于kurento media server的多人视频聊天示例相关推荐

  1. 基于FMS(Flash Media Service)的多人视频聊天

    记得以前在XP系统上做过一个基于Flex和Fms的多人视频程序,但是最近把它拿过来放在我的Win7的本里面运行竟然总是报告NetConnection.Call.Failed弄得我莫名奇妙,又有点不知所 ...

  2. 主流Webrtc流媒体服务器之Kurento Media Server

    一. 什么是Kurento Media Server Kurento Media Server(KMS)是一个多媒体服务器软件包,可用于为WebRTC平台开发高级视频应用程序.Kurento是一个底层 ...

  3. Kurento Media Server源码编译

    前言 文章首发于Noah Sun's Home Kurento Media Server源码编译 Build dependency Module dependency graph Kurento模块的 ...

  4. 【从头到脚】撸一个多人视频聊天 — 前端 WebRTC 实战(一)

    前言 [ 从头到脚 ]会作为一个系列文章来发布,它包括但不限于 WebRTC 多人视频,预计会有: WebRTC 实战(一):也就是本期,主要是基础讲解以及一对一的本地对等连接,网络对等连接. Web ...

  5. WebRTC实现多人视频聊天

    写在前面 实现房间内人员的视频聊天,由于并未很完善,所以需要严格按照步骤来,当然基于此完善,就是时间的问题了. 架构 整个设计架构如下: 图片来自于参考博文.我使用的是第一种Mesh 架构,无需任何流 ...

  6. 从头到脚撸一个多人视频聊天 — WebRTC 实战(一)

    作者:江三疯,知乎.掘金账号同名,点击阅读原文查看作者 github. 前言 [ 从头到脚 ]会作为一个系列文章来发布,它包括但不限于 WebRTC 多人视频,预计会有: WebRTC 实战(一):也 ...

  7. 实践:《从头到脚撸一个多人视频聊天 — 前端 WebRTC 实战(一)》

    2019独角兽企业重金招聘Python工程师标准>>> 请先阅读原文,链接:从头到脚撸一个多人视频聊天 - 前端 WebRTC 实战(一),本文只涉及实践过程中的问题 1.video ...

  8. 教你用WebRTC撸一个多人视频聊天

    之前公司准备用 webRTC 来实现视频聊天,研究了几天,撸了个 demo 出来,(虽然最后并没有采用这项技术,囧),但是还是写一个出来吧! WebRTC简单介绍 WebRTC (Web Real-T ...

  9. 【从头到脚】前端实现多人视频聊天— WebRTC 实战(多人篇)

      猛戳关注 前端发动机,嘿嘿嘿! 前言 这是 WebRTC 系列的第三篇文章,主要讲多人点对点连接.如果你对 WebRTC 还不太了解,推荐阅读我之前的文章. •WebRTC 基础及 1 v 1 对 ...

最新文章

  1. spark之scala快速入门
  2. SAP QM高阶之检验点(Inspection Point)
  3. 区块链开发指南_区块链钱包开发
  4. [转]删除MSSQL所有的约束及表格
  5. 进程间通信——自定义消息方式实现(SetWindowsHookEx)
  6. leetcode-387-字符串中的第一个唯一字符
  7. 从零入门Serverless|一文搞懂函数计算及其工作原理
  8. WCF 第二章 契约 系列文章
  9. Jenkins 内部服务器遭访问且被部署密币挖机
  10. Dlubal RFEM(有限元分析软件)官方中文版V5.25.01 | 结构设计软件下载 | 有限元分析软件有哪些
  11. 霍尔在光伏发电系统中的应用与产品选型
  12. 焦虑的时候听一听,分享我喜欢的一段话,心就安静很多
  13. Android 模拟器 Root 和 SuperSU 安装
  14. Microsoft FxCop 的设计规则 .
  15. html中qq号码怎么写,根据QQ头像hash分析QQ号(html版)
  16. 程序员进 ICU 昏迷五六天,恢复后决定辞职回乡改行送菜
  17. 小米体脂秤2内部方案一览,附拆解维修记录
  18. Flutter必备——Dart入门(下)
  19. 字符串与Date类型相互转换
  20. 《软件技术基础》之《操作系统习题解析》

热门文章

  1. 网站管理助手4.0 mysql_网站管理助手v4.0 建站流程
  2. fft画图出现乱直线情况
  3. EMC设计中的”猪尾巴效应“
  4. Python数据分析:实时更新全国全球疫情分析
  5. 统计英文字符串里重复次数最多的单词JAVA
  6. INT3断点和硬件断点
  7. 美的地产竞得石家庄50亩地块 美的布局开始
  8. 百格活动告诉你为什么你始终做不好活动营销?
  9. python3 paramiko 远程执行 ssh 命令、上传文件、下载文件
  10. 可编程逻辑器件与专用集成电路——《Verilog 与数字ASIC设计基础》读书笔记