1 Star 3 Fork 2

LiamHao/joker

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
web-rtc-websocket.blade.php 11.85 KB
一键复制 编辑 原始数据 按行查看 历史
liamhao 提交于 2021-04-21 13:25 +08:00 . 添加https
<!DOCTYPE html>
<html>
<head>
<title>WebRTC Websocket</title>
<!-- 引入laravel-echo工具,其实使用Larave自带的也可以。但是,使用自带的还需要用到node前端构建工具,我这里只简单的演示后端实现过程,就不用node了 -->
<script src="https://cdn.jsdelivr.net/npm/laravel-echo@1.10.0/dist/echo.iife.js"></script>
<!-- 引入pusher工具,pusher是Laravel-echo底层,Laravel-echo是pusher的一层封装 -->
<script src="https://cdn.jsdelivr.net/npm/pusher-js@7.0.3/dist/web/pusher.min.js"></script>
</head>
<body>
<div id="remote-video-area">
</div>
远程窗口⬆
<hr>
本地窗口⬇
<div>
<video class="local-video" controls autoplay playsinline style="width: 400px;"></video>
<button id="show-local">抓取本地窗口</button>
<button id="join-room">加入房间</button>
<button id="call-remote">发起远程视频请求</button>
<button id="close-remote">关闭远程视频连接</button>
<button id="leave-room">离开房间</button>
</div>
<script type="text/javascript">
// 参考文献:https://huangxiaoguo.blog.csdn.net/article/details/104229233
// 当前登陆用户信息,正常的话是需要从 session 中取的,为了方便就临时写死了
var me = {
id: '{{ request()->user()->id }}',
name: '{{ request()->user()->name }}',
}
// 通信方式:websocket,使用 Laravel-echo + Laravel-websockets
// 为了方便,使用 whisper() 方法进行广播
var wsHost = location.hostname;
var wsPort = 2020;
var channel = 'chatting-room';
var laravelEcho = new Echo({
broadcaster: 'pusher',
key: 'joker',
wsHost: wsHost,
wsPort: wsPort,
wssPort: wsPort,
forceTLS: false,
enabledTransports: ['ws', 'wss'],
});
// 本地媒体流对象,这里说的是「媒体」流,其包括「视频」流和「音频」流
var localStream = null;
// PeerConnection:对等连接,为通信的双方提供一个相同的接口/协议
var pc = null;
// 多人视频时的 pc 管理
var pcStore = [];
// 对等连接配置
var pcConfig = {
'iceServers':[
{
'urls':'stun:'+wsHost+':'+wsPort,
}
]
}
// 本地视频窗口对象
var localVideo = document.querySelector('.local-video');
// 发起媒体连接请求
var call = function(me){
// 要求浏览器正确构建一个 SDP(会话描述协议)对象,该对象代表发起方的媒体和要传达给远程方的功能
getPc(me).createOffer({
offerToReceiveAudio: true,
offerToReceiveVideo: true,
})
.then((description) => {
// 将这个 SDP 对象设为本地会话描述协议
getPc(me).setLocalDescription(description);
// 并将这个会话描述协议广播给当前房间的所有人
laravelEcho.join(channel).whisper('VS', {
user: me,
description: description,
});
})
.catch((err) => {
console.log(err);
});
}
// 创建一个对等连接
var createPeerConnection = function(user){
// 初始化对等连接
pc = new RTCPeerConnection(pcConfig);
// 设置回调方法,每当浏览器内部的 ICE 协议机器将新候选者提供给本地对等方(调用 setLocalDescription() 方法)时,就会触发 onicecandidate 处理程序。
pc.onicecandidate = function(e){
if(e.candidate){
// 如果事件中有「候选人」参数,则通过 websocket 广播将「候选人」信息给当前房间内的所有人。
laravelEcho.join(channel).whisper('VS', {
user: user,
description: {
type: 'candidate',
candidate: e.candidate,
},
});
}
}
pc.onconnectionstatechange = function(event) {
console.log(pc.connectionState);
switch (pc.connectionState) {
// 新建
case 'new':
break;
// 连接中
case 'connecting':
break;
// 连接成功,添加对应的 video 标签
case 'connected':
var newVideoHtml = document.createElement('video');
newVideoHtml.id = 'remote-video-'+pc.getRemoteStreams()[0].id;
newVideoHtml.controls = true;
newVideoHtml.autoplay = true;
newVideoHtml.playsinline = true;
newVideoHtml.style = "width: 800px;";
document.getElementById('remote-video-area').appendChild(newVideoHtml);
// 将事件中的远程媒体流赋值给「远程视频窗口」并自动播放
document.getElementById(newVideoHtml.id).srcObject = pc.getRemoteStreams()[0];
break;
// 断开连接
case 'disconnected':
document.getElementById('remote-video-'+pc.getRemoteStreams()[0].id).remove();
break;
// 连接关闭,删除掉对应的 video 标签
case 'failed':
break;
}
};
// 设置回调方法,当调用 addTrack() 方法时会触发 ontrack 处理程序。
pc.ontrack = function(e){
console.log(e);
}
// 获取全部的媒体列表,并将这些媒体的源改为本地媒体流对象
if(localStream){
localStream.getTracks().forEach((track)=>{
pc.addTrack(track,localStream);
});
}
pcStore[user.id] = pc;
return pc;
}
// 关闭对等连接
var closePeerConnection = function (user){
if(pcStore[user.id]){
// 关闭远程窗口
document.getElementById('remote-video-'+pcStore[user.id].getRemoteStreams()[0].id).remove();
pcStore[user.id].close();
pcStore[user.id] = null;
}
}
var getPc = function(user){
return pcStore[user.id] ? pcStore[user.id] : createPeerConnection(user);
}
// 关闭本地媒体流
var closeLocalMedia = function (){
if (localStream) {
localStream.getTracks().forEach((track)=>{
track.stop();
});
}
localStream = null;
}
// 开始连接
var conn = function(){
// 选择房间
laravelEcho.join(channel)
.here((users) => {
// 当前用户加入频道时触发,返回当前频道中在线的人员列表
console.log("here");
console.table(users);
// 创建对等连接
users.forEach((user) => {
getPc(user);
});
})
.joining((user) => {
// 其他人加入频道时会触发,返回加入人的信息
console.log("joining");
})
.leaving((user) => {
// 其他人离开频道时会触发,返回离开人的信息
console.log("leaving");
// 关闭对等连接
closePeerConnection(user);
// closeLocalMedia();
})
.listenForWhisper('VS', (data) => {
// 监听事件,事件名称自定义,触发和监听保持一致即可。
if(data.description){
if(data.description.type === 'offer'){
console.log(data);
if(confirm('请求远程连接...')){
// 如果会话描述协议的类型 (type) 是媒体连接请求
// 通过会话描述协议创建一个新的 RTCSessionDescription 描述协议
// 并将这个新描述协议设为远程连接描述协议
getPc(me).setRemoteDescription(new RTCSessionDescription(data.description));
// 根据新的远程连接描述协议生成应答会话描述协议
getPc(me).createAnswer().then((description) => {
// 将这个应答会话描述协议设为本地会话描述协议
getPc(me).setLocalDescription(description);
// 并将这个应答会话描述协议广播给当前房间的所有人
laravelEcho.join(channel).whisper('VS', {
user: me,
description: description,
});
})
.catch((err) => {
console.log(err);
});
}
} else if(data.description.type === 'answer'){
// 如果会话描述协议的类型 (type) 是应答
// 通过应答会话描述协议创建一个新的 RTCSessionDescription 描述协议
// 并将这个新描述协议设为远程连接描述协议(经过一问一答,双方的远程连接描述协议已经保持一致)
getPc(me).setRemoteDescription(new RTCSessionDescription(data.description));
} else if(data.description.type === 'candidate'){
// 如果会话描述协议的类型 (type) 是候选人
// 向 ICE 代理添加远程候选对象
getPc(me).addIceCandidate(new RTCIceCandidate(data.description.candidate));
} else {
console.log('the message is invalid!',data)
}
}
});
}
document.getElementById('show-local').onclick = function(event) {
// 调用 Chrome 浏览器的 API,获取媒体设备(显示器、摄像头、麦克风等)
navigator.mediaDevices
// 获取屏幕设备的图像和音频
.getDisplayMedia({
video: true,
audio: true,
})
.then((stream) => {
// 将媒体流存入变量
localStream = stream;
// 将媒体流放入页面中的「本地视频」窗口
localVideo.srcObject = localStream;
})
.catch((e) => {
console.log(e);
});
}
document.getElementById('join-room').onclick = function(event) {
// 这个函数的调用时机特别重要,一定要在获取到本地媒体流之后再调用,否则会出现绑定失败的情况
conn();
};
document.getElementById('call-remote').onclick = function(event) {
call(me);
};
document.getElementById('close-remote').onclick = function(event) {
closePeerConnection(me);
};
document.getElementById('leave-room').onclick = function(event) {
laravelEcho.leave(channel);
};
</script>
</body>
</html>
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/liamhao/joker.git
git@gitee.com:liamhao/joker.git
liamhao
joker
joker
master

搜索帮助