网页版WebRTC多人聊天Demo

网页版WebRTC多人聊天Demo
本文基于Codelab中step7,在其基础上作简单修改,使其支持多人视频通讯,本文暂时只支持星状结构三人聊天,多人聊天可以在基础上扩展,原理相同。
一.源码分析
该工程包括三个文件:server.js,main.js,index.html。
1.server.js
if (numClients == 0){
            socket.join(room);
            socket.emit(‘created‘, room);
        } else if (numClients == 1) {
            io.sockets.in(room).emit(‘join‘, room);
            socket.join(room);
            socket.emit(‘joined‘, room);
        } else { // max two clients
            socket.emit(‘full‘, room);
        }
        socket.emit(‘emit(): client ‘ + socket.id + ‘ joined room ‘ + room);
        socket.broadcast.emit(‘broadcast(): client ‘ + socket.id + ‘ joined room ‘ + room);
后台服务代码,负责异步消息通讯。当有新用户加入房间时,向客户端发送消息,客户端接收到消息后作相应的处理。
2.index.html
网站主页,包括两块视频区域和文本区域。


<!DOCTYPE html>
<html>
<head>

<meta name=‘keywords‘ content=‘WebRTC, HTML5, JavaScript‘ />
<meta name=‘description‘ content=‘WebRTC Reference App‘ />
<meta name=‘viewport‘ content=‘width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1‘>

<base target=‘_blank‘>

<title>WebRTC client</title>

<link rel=‘stylesheet‘ href=‘css/main.css‘ /> 

</head>

<body>

<div id=‘container‘ class=‘main‘ >

  <div id=‘videos‘ class=‘videos‘>
    <video id=‘localVideo‘ class=‘localVideo‘ autoplay muted></video>
    <video id=‘remoteVideo‘ class=‘remoteVideo‘ autoplay></video>
  </div>

  <div id=‘textareas‘>
        <textarea id="dataChannelSend" disabled placeholder="Press Start, enter some text, then press Send."></textarea>
        <textarea id="dataChannelReceive" disabled></textarea>
    </div>

  <button id="sendButton" disabled>Send</button>

</div>

<script src=‘/socket.io/socket.io.js‘></script>
<script src=‘js/lib/adapter.js‘></script>
<script src=‘js/main.js‘></script>

</body>
</html>
3.main.js
核心代码区域,包括房间的创建,RTCPeerConnection创建和两点间的视频通话。
3.1消息处理
socket.on(‘created‘, function (room){
  console.log(‘Created room ‘ + room);
  isInitiator = true;
});

socket.on(‘full‘, function (room){
  console.log(‘Room ‘ + room + ‘ is full‘);
});

socket.on(‘join‘, function (room){
  console.log(‘Another peer made a request to join room ‘ + room);
  console.log(‘This peer is the initiator of room ‘ + room + ‘!‘);
  isChannelReady = true;
});

socket.on(‘joined‘, function (room){
  console.log(‘This peer has joined room ‘ + room);
  isChannelReady = true;
});

socket.on(‘message‘, function (message){
  console.log(‘Received message:‘, message);
  if (message === ‘got user media‘) {
      maybeStart();
  } else if (message.type === ‘offer‘) {
    if (!isInitiator && !isStarted) {
      maybeStart();
    }
    pc.setRemoteDescription(new RTCSessionDescription(message));
    doAnswer();
  } else if (message.type === ‘answer‘ && isStarted) {
    pc.setRemoteDescription(new RTCSessionDescription(message));
  } else if (message.type === ‘candidate‘ && isStarted) {
    var candidate = new RTCIceCandidate({sdpMLineIndex:message.label,
      candidate:message.candidate});
    pc.addIceCandidate(candidate);
  } else if (message === ‘bye‘ && isStarted) {
    handleRemoteHangup();
  }
});

3.2peerconnection创建和通讯
function createPeerConnection() {
  try {
    pc = new RTCPeerConnection(pc_config, pc_constraints);
    pc.onicecandidate = handleIceCandidate;
    console.log(‘Created RTCPeerConnnection with:\n‘ +
      ‘  config: \‘‘ + JSON.stringify(pc_config) + ‘\‘;\n‘ +
      ‘  constraints: \‘‘ + JSON.stringify(pc_constraints) + ‘\‘.‘);
  } catch (e) {
    console.log(‘Failed to create PeerConnection, exception: ‘ + e.message);
    alert(‘Cannot create RTCPeerConnection object.‘);
      return;
  }
  pc.onaddstream = handleRemoteStreamAdded;
  pc.onremovestream = handleRemoteStreamRemoved;

  if (isInitiator) {
    try {
      // Reliable Data Channels not yet supported in Chrome
      sendChannel = pc.createDataChannel("sendDataChannel",
        {reliable: false});
      sendChannel.onmessage = handleMessage;
      trace(‘Created send data channel‘);
    } catch (e) {
      alert(‘Failed to create data channel. ‘ +
            ‘You need Chrome M25 or later with RtpDataChannel enabled‘);
      trace(‘createDataChannel() failed with exception: ‘ + e.message);
    }
    sendChannel.onopen = handleSendChannelStateChange;
    sendChannel.onclose = handleSendChannelStateChange;
    console.log(‘....................this is  a initiator = true....................‘);
  } else {
    pc.ondatachannel = gotReceiveChannel;
    console.log(‘....................this is not a initiator = false....................‘);
  }
}

3.3 视频源的输出展现


function handleRemoteStreamAdded(event) {
  console.log(‘Remote stream added.‘);
 // reattachMediaStream(miniVideo, localVideo);
  attachMediaStream(remoteVideo, event.stream);
  remoteStream = event.stream;
//  waitForRemoteVideo();
}

二.
简单工作流程介绍与修改思路

1. 工作过程如下:

1.1.浏览器A访问主页,允许访问摄像头音频设备,server接收到‘create or join‘消息,计算此时连接到服务器的客户端数量,此时数量为0,则向客户端发送‘created‘消息。
1.2.浏览器A接收到‘created‘消息,将isInitiator设为true,该值为true表示该客户断是peerconnection的发起者。
1.3.浏览器B访问主页,允许访问摄像头音频设备,server接收到‘create or join‘消息,计算此时连接到服务器的客户端数量,此时数量为1,则向客户端发送join和joined消息。
1.4.浏览器A和浏览器B都接收到join和joined消息,设置isChannelReady=true,表示此时准备好建立连接。浏览器A发起peerconnection连接doCall,浏览器B回应peerconnection连接doAnswer,A和B建立P2P连接。
1.5.A和B分别将来自本地和远端的视频stream显示在页面上。
注意:浏览器A和浏览器B都接受来自server相同的消息,而两者在接收到相同的消息后的处理却不一样(main.js代码是一样的),一个是发起者,一个是应答者。可以使用状态机来理解,程序所处状态不一样,虽然接收到相同的命令,但可以做出不同的处理(通过isInitiator变量区分不同的状态)。

2.三人聊天室的实现
简单起见,我们暂时先实现三人视频通讯,使用星状结构。下面是修改思路:
a.A和B以及建立连接,此时如C加入,可以将A和C建立连接,同时保持A和B之前的连接。此时,A能看到B和C,而B和C只能看到A。
b.如果A B C三者需要互相看到,则需要A将B的视频传给C,并将C的视频传给B。

本文暂时只实现A与B通讯,A与C通讯,BC之间不能通讯。下面是具体的代码修改步骤:
2.1server.js


if (numClients == 0){
            socket.join(room);
            socket.emit(‘created‘, room);
        } else if (numClients <=2 ) { //第三个用户加入后仍然发送join joined消息
            io.sockets.in(room).emit(‘join‘, room);
            socket.join(room);
            socket.emit(‘joined‘, room);
        } else { // max two clients
            socket.emit(‘full‘, room);
        }

2.2index.html
可以采用动态方式添加,这里简单起见直接增加一路视频实现块。
<div id=‘videos‘ class=‘videos‘>
    <video id=‘localVideo‘ class=‘localVideo‘ autoplay muted></video> //本地视频 A
  </div>
  <div >
    <video id=‘remoteVideo‘ class=‘remoteVideo‘ autoplay></video>// remote视频B
  </div>
  <div >
    <video id=‘remoteVideo2‘ class=‘remoteVideo2‘ autoplay></video> //remote视频c
  </div>

2.3 main.js
   a.增加一个全局变量isPeerEstablished
用来表示该客户端是否已经创建了PeerConnection。isPeerEstablished和isInitiator两者可以区分发起者和应答者,因为具有超过2个客户端,所以必须使用isPeerEstablished来选择尚未创建连接的客户端作为应答者。
     var isPeerEstablished=false;
   b.处理message机制修改
在判断条件里面加入(!isPeerEstablished||isInitiator)表示尚未创建链接C和发起者A才会执行peerconnection。保证新加入者C和A创建链接,同时保持A和B的连接。
socket.on(‘message‘, function (message){
  console.log(‘Received message:‘, message);
  if (message === ‘got user media‘&&(!isPeerEstablished||isInitiator)) {
    maybeStart();
  } else if (message.type === ‘offer‘&&(!isPeerEstablished||isInitiator)) {
    if (!isInitiator && !isStarted) {
      maybeStart();
    }
    pc.setRemoteDescription(new RTCSessionDescription(message));
    doAnswer();
  } else if (message.type === ‘answer‘ && isStarted&&(!isPeerEstablished||isInitiator)) {
    pc.setRemoteDescription(new RTCSessionDescription(message));
  } else if (message.type === ‘candidate‘ && isStarted&&(!isPeerEstablished||isInitiator)) {
    var candidate = new RTCIceCandidate({sdpMLineIndex:message.label,
      candidate:message.candidate});
    pc.addIceCandidate(candidate);
  } else if (message === ‘bye‘ && isStarted) {
    handleRemoteHangup();
  }
});

c.视频流展现

如果isInitiator和isPeerEstablished都为true,说明此时A和B已经建立链接。此时,应该将新的视频流显示在remoteVideo2中。其他情况将视频流展示在remoteVideo中。
function handleRemoteStreamAdded(event) {
  console.log(‘Remote stream added.‘);

 // reattachMediaStream(miniVideo, localVideo);
if(isInitiator&&isPeerEstablished){
  attachMediaStream(remoteVideo2, event.stream);
  remoteStream2 = event.stream;
}else{
  attachMediaStream(remoteVideo, event.stream);
  remoteStream = event.stream;
}
isPeerEstablished=true;
//  waitForRemoteVideo();
}

d.其他两处修改
var remoteVideo2 = document.querySelector(‘#remoteVideo2‘);

......

function handleRemoteHangup() {
  console.log(‘Session terminated.‘);
  stop();
  //isInitiator = false;  //总是保持A的发起者角色

}
三人聊天效果图:

				
时间: 2024-08-07 04:32:43

网页版WebRTC多人聊天Demo的相关文章

vue仿微信网页版|vue+web端聊天室|仿微信客户端vue版

一.项目介绍 基于Vue2.5.6+Vuex+vue-cli+vue-router+vue-gemini-scrollbar+swiper+elementUI等技术混合架构开发的仿微信web端聊天室——vueWebChat,实现了发送消息.表情(动图),图片.视频预览,右键菜单.截屏.截图可直接粘贴至文本框进行发送. 二.技术框架 MVVM框架:Vue2.5.6 状态管理:Vuex 页面路由:Vue-router iconfont图标:阿里巴巴字体图标库 自定义滚动条:vue-gemini-sc

一键安装JS SDK 网页版WebRTC 网页 SIP客户端 语音通话,可以做web坐席

目录服务器环境要求 1下载脚本文件并安装 1修改绑定的域名 1替换域名证书 2重启Web 2呼叫测试 3 服务器环境要求CentOS 6.9 64bit 下载脚本文件并安装wget http://voip-sdk.oss-cn-beijing.aliyuncs.com/rtc_sip/install_rtc_sip.sh wget http://voip-sdk.oss-cn-beijing.aliyuncs.com/rtc_sip/python_install.sh #yum -y insta

3分钟实现网页版多人文本、视频聊天室 (含完整源码)

基于SimpleWebRTC快速实现网页版的多人文本.视频聊天室. 1 实现方法 复制下面的代码,保存为一个html文件 <!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://simplewebrtc.com/latest.js"

Android多人视频聊天应用的开发(三)多人聊天

在上一篇<Android多人视频聊天应用的开发(二)一对一聊天>中我们学习了如何使用声网Agora SDK进行一对一的聊天,本篇主要讨论如何使用Agora SDK进行多人聊天.主要需要实现以下功能: 1.上一篇已经实现过的聊天功能 2.随着加入人数和他们的手机摄像头分辨率的变化,显示不同的UI,即所谓的"分屏" 3.点击分屏中的小窗,可以放大显示该聊天窗 分屏 根据前期技术调研,分屏显示最好的方式是采用瀑布流结合动态聊天窗实现,这样比较方便的能够适应UI的变化.所谓瀑布流,

基于WebSocket实现网页版聊天室

WebSocket ,HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,其使用简单,应用场景也广泛,不同开发语言都用种类繁多的实现,仅Java体系中,Tomcat,Jetty,Spring等都提供了对WS的API支持.本篇不做理论探究,仅自娱自乐,简单实现网页版的聊天室功能,在实际开发场景中变通使用即可.废话不叽歪,直接撸出来—— 1  简单页面 <!DOCTYPE html> <html lang="en"> <head> &l

分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”

这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业不是百度来的,我只是百度了一些示例代码的意思,怎么用!算了,越解释万一越黑呢!哈哈O(∩_∩)O哈哈~) ----------------------------------------------------------------分界线------------------------------

WebSocket入门教程(五)-- WebSocket实例:简单多人聊天室

from:https://blog.csdn.net/u010136741/article/details/51612594 [总目录] WebSocket入门教程--大纲 [实例简介] 本文,我们通过nodejs和javascript实现一个网页聊天室的demo.主要包括,聊天,改用户名,查看其他用户在线状态的功能.大致流程为,用户访问网页,即进入聊天状态,成为新游客,通过底部的输入框,可以输入自己想说的话,点击发布,信息呈现给所有在聊天的人的页面.用户可以实时修改自己的昵称,用户离线上线都会

基于Node.js + socket.io实现WebSocket的聊天DEMO

原文摘自我的前端博客,欢迎大家来访问 http://hacke2.github.io 简介 最近看Node.js和HTML5,练手了一个简易版的聊天DEMO,娱乐一下 为什么需要socket.io? node.js提供了高效的服务端运行环境,但是由于浏览器端对HTML5的支持不一, 为了兼容所有浏览器,提供卓越的实时的用户体验,并且为程序员提供客户端与服务端一致的编程体验, 于是socket.io诞生. 简答来说socket.io具体以下特点: 1.socket.io设计的目标是支持任何的浏览器

微信网页版朋友圈在哪?怎么找不到

之前ytkah有介绍了微信网页版,确实方便了很多,直接浏览器就可以操作,不用安装其他程序.用过的朋友都在问微信网页版朋友圈在哪?现在我们一起来找找吧 如下图所示,左侧:第一行的右上角是菜单,分别是发起聊天.关闭桌面通知.关闭声音.意见反馈.退出:第二行是搜索框,可以搜索微信好友.公众号.聊天记录:第三行是微信聊天列表.公众号列表.通讯录列表:右侧:聊天窗口.公众号文章显示.通讯录显示 比较遗憾,微信网页版朋友圈这个功能确实找不到,也许微信团队原本就没打算把它加进去,本着极简的原则,让你提高效率,