WebRTC项目一对一视频

news2024/11/15 2:23:19

开发步骤


1.客户端显示界面
2.打开摄像头并显示到页面
3.websocket连接
4.join、new-peer、resp-join信令实现
5.leave、peer-leave信令实现
6.offer、answer、candidate信令实现
7.综合调试和完善

1.客户端显示界面

步骤:创建html页面
主要是input、button、video控件的布局。
 


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webrtc-demo</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
            margin: 0;
            padding: 20px;
            text-align: center;
        }
        h1 {
            color: #333;
        }
        #buttons {
            margin-bottom: 20px;
        }
        input[type="text"] {
            padding: 10px;
            width: 250px;
            border: 1px solid #ccc;
            border-radius: 5px;
            margin-right: 10px;
        }
        button {
            padding: 10px 15px;
            border: none;
            border-radius: 5px;
            background-color: #4CAF50;
            color: white;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        button:hover {
            background-color: #45a049;
        }
        #videos {
            display: flex;
            justify-content: center;
            margin-top: 20px;
        }
        video {
            border: 2px solid #ccc;
            border-radius: 5px;
            margin: 0 10px;
            width: 600px; /* 增加视频宽度 */
            height: 400px; /* 设置视频高度 */
        }
    </style>
</head>
<body>
    <h1>webrtc</h1>
    <div id="buttons">
        <input id="roomid" type="text" placeholder="请输入房间id" maxlength="50"/>
        <button id="joinBtn" type="button">加入房间</button>
        <button id="leaveBtn" type="button">离开房间</button>
    </div>

    <div id="videos">
        <video id="localVideo" autoplay playsinline muted controls>本地视频窗口</video>
        <video id="remoteVideo" autoplay playsinline muted controls>远程视频窗口</video>
    </div>

    <script type="module" src="js/main.js"></script>
    <script type="module" src="js/adapter-latest.js"></script>
</body>
</html>

2.打开摄像头并显示到页面

function openLocalStream() {
    //初始化本地码流
    navigator.mediaDevices
    .getUserMedia({ //初始化本地码流信息
        audio: true,
        video: { width: 640, height: 480 }
    })
    .then(function (stream) { // 打开本地码流
        console.log('Open local stream');
        localVideo.srcObject = stream;
        localStream = stream;
    })
    .catch(function (e) { //错误cb
        alert("getUserMedia() error: " + e.name);
    });
}
document.getElementById('joinBtn').onclick = function () {
    console.log("加入按钮被点击");
    openLocalStream();
};


function closeLocalStream() {
    if (localStream) {
        localStream.getTracks().forEach(track => {
            track.stop();
        });
        localStream = null;
        localVideo.srcObject = null;
    }
}
document.getElementById('leaveBtn').onclick = function () {
    console.log("离开按钮被点击");
    closeLocalStream();
};

3.websocket连接

 封装websocket 

class RTCEngine {
    constructor(wsUrl) {
        this.wsUrl = wsUrl;
        this.signaling = null;
        this.createWebSocket();
    }

    createWebSocket() {
        this.signaling = new WebSocket(this.wsUrl);
        this.signaling.onopen = () => this.onOpen();
        this.signaling.onmessage = (ev) => this.onMessage(ev);
        this.signaling.onerror = (ev) => this.onError(ev);
        this.signaling.onclose = (ev) => this.onClose(ev);
    }

    onOpen() {
        console.log("WebSocket opened.");
        //开启心跳包
        this.timerId = setInterval(() => {
            var jsonMsg = {
              'cmd': SIGNAL_TYPE_HEARTBEAT,
              'type': 'active'
            };
            this.sendMessage(JSON.stringify(jsonMsg));
          }, HEARTBEAT_INTERVAL);

    }

    onError(event) {
        console.log("onError: " + event.data);
    }

    onClose(event) {
        console.log("onClose -> code: " + event.code + ", reason: " + event.reason);
        //关闭心跳包
        clearInterval(conn.timerId);
    }

    sendMessage(message) {
        this.signaling.send(message);
    }
    hanleSendHeartBeat(message) {
        var jsonMsg = {
            'cmd': SIGNAL_TYPE_HEARTBEAT,
            'type':'ok'
        };
        this.sendMessage(JSON.stringify(jsonMsg));
    }
    onMessage(event) {
        console.log("onMessage: " + event.data);
        let jsonMsg;
        try {
            jsonMsg = JSON.parse(event.data);
        } catch (e) {
            console.warn("onMessage parse JSON failed: " + e);
            return;
        }

        // 处理从服务器接收到的消息
        switch (jsonMsg.cmd) {
            case SIGNAL_TYPE_NEW_PEER:
                handleRemoteNewPeer(jsonMsg); //doOffer
                break;
            case SIGNAL_TYPE_RESP_JOIN:
                handleResponseJoin(jsonMsg);
                break;
            case SIGNAL_TYPE_PEER_LEAVE:
                handleRemotePeerLeave(jsonMsg);
                break;
            case SIGNAL_TYPE_OFFER:
                handleRemoteOffer(jsonMsg); //doAnswer
                break;
            case SIGNAL_TYPE_ANSWER:
                handleRemoteAnswer(jsonMsg);
                break;
            case SIGNAL_TYPE_CANDIDATE:
                handleRemoteCandidate(jsonMsg);
                break
            case SIGNAL_TYPE_OVERLOAD:
                alert("房间号人数已满,请稍后再试");
                break;
            case SIGNAL_TYPE_HEARTBEAT:
                if(jsonMsg.type == 'active')
                    this.hanleSendHeartBeat(jsonMsg);
                //console.log("收到心跳包:"+jsonMsg.type);
                break;
            default:
                console.warn("unknown cmd: " + jsonMsg.cmd);
                break;
        }
    }
}

// 实例化 RTCEngine
const rtcEngine = new RTCEngine(SERVERADDR);

4.join、new-peer、resp-join overload信令设计

信令设计:
RTCEngineWS.js  websocket封装类负责接收,发送消息以及连接状态管理。
signal.js   信令的设计集合
main.js 主逻辑
思路:(1)点击加入开妞;(2)响应加入按钮事件;(3)将join发送给服务器;(4)服务器 根据当前房间的人数
做处理,如果房间已经有人则通知房间里面的人有新人加入(newpeer),并通知自己房间里面是什么人(resp-join)。如果房间存在2个 则返回房间号人数已满


var jsonMsg = {
    'cmd': 'join',
    'roomId': roomId,
    'uid': localUserId,
};
var jsonMsg = {
    'cmd': 'resp‐join',
    'remoteUid': remoteUid
};
var jsonMsg = {
   'cmd': 'new‐peer',
   'remoteUid': uid
};
var jsonMsg = {
    'cmd': 'overload',
};

5.leave、peer-leave 信令实现

思路:(1)点击离开按钮;(2)响应离开按钮事件;(3)将leave发送给服务器;(4)服
务器处理leave,将发送者删除并通知房间(peer leave)的其他人;(5)房间的其他人在客户
端响应peer leave事件。

var jsonMsg = {
    'cmd': 'leave',
    'roomId': roomId,
    'uid': localUserId,
};
var jsonMsg = {
    'cmd': 'peer‐leave',
    'remoteUid': uid
};
var jsonMsg = {
    'cmd': 'peer‐leave',
    'remoteUid': uid
}; 

 6.offer、answer、candidate信令实现

 思路:
(1C)收到newpeer (handleRemoteNewPeer处理),作为发起者创建RTCPeerConnection,绑定事件响应函数,加入本地流;
(2C)创建offer sdp,设置本地sdp,并将offer sdp发送到服务器;
(3SSS)服务器收到offer sdp 转发给指定的remoteClient;
(4C)接收者收到offer,也创建RTCPeerConnection,绑定事件响应函数,加入本地流;
(5C)接收者设置远程sdp,并创建answer sdp,然后设置本地sdp并将answer sdp发送到服务器;
(6SSS)服务器收到answer sdp 转发给指定的remoteClient;
(7C)发起者收到answer sdp,则设置远程sdp;
(8C)发起者和接收者都收到ontrack回调事件,获取到对方码流的对象句柄;

(9)发起者和接收者都开始请求打洞,通过onIceCandidate获取到打洞信息(candidate)并发送给对方
(10)如果P2P能成功则进行P2P通话,如果P2P不成功则进行中继转发通话。

offer、answer、candidate分别如下
var jsonMsg = {
    'cmd': SIGNAL_TYPE_OFFER,
    'roomId': roomId,
    'uid': localUserId,
    'remoteUid': remoteUserId,
    'msg': JSON.stringify(session)
};
var jsonMsg = {
    'cmd': SIGNAL_TYPE_ANSWER,
    'roomId': roomId,
    'uid': localUserId,
    'remoteUid': remoteUserId,
    'msg': JSON.stringify(session)
};
var jsonMsg = {
    'cmd': SIGNAL_TYPE_CANDIDATE,
    'roomId': roomId,
    'uid': localUserId,
    'remoteUid': remoteUserId,
    'msg': JSON.stringify(event.candidate)
};

 

7.综合调试和完善

正常退出和关闭工作

思路:

(1)点击离开时,要将本地摄像头和麦克风关闭; 要将RTCPeerConnection关闭(close);

(2)检测到客户端退出时,服务器再次检测该客户端是否已经退出房间。

总结

信令服务器设计,主要是转发客户端发来的消息到对端,根据类型进行转发。


 

项目链接

jbj62/webrtc-demo - 码云 - 开源中国

学习资料分享

0voice · GitHub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2240540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

启动本地开发环境(自带热启动)yarn serve

文章目录 1. 安装 Yarn使用 npm 安装 Yarn使用 Chocolatey 安装 Yarn&#xff08;Windows 用户&#xff09;使用 Homebrew 安装 Yarn&#xff08;macOS 用户&#xff09; 2. 安装项目依赖3. 启动项目开发模式启动生产模式启动 4. 构建项目开发模式构建生产模式构建 5. 其他常用…

SpringCloud框架学习(第二部分:Consul、LoadBalancer和openFeign)

目录 六、Consul服务注册和发现 1.基本介绍 2.下载运行 3.服务注册与发现 &#xff08;1&#xff09;支付服务provider8001注册进consul &#xff08;2&#xff09;修改订单服务cloud-consumer-order80 4.CAP &#xff08;1&#xff09;CAP理论 &#xff08;2&#x…

SAP ABAP开发学习记录——报表选择界面初始值

程序中定义选择界面的部分只是创建输入框&#xff0c;在后续使用中需要自行添加搜索条件&#xff0c;而有关时间或者日期这种&#xff0c;希望自动创建一个默认值&#xff0c;有两种方法&#xff0c;一种是在选择界面初始化时增加语句另外一种是通过在选择界面创建变式实现。 …

16.UE5拉怪机制,怪物攻击玩家,伤害源,修复原视频中的BUG

2-18 拉怪机制&#xff0c;怪物攻击玩家、伤害源、黑板_哔哩哔哩_bilibili 目录 1.实行行为树实现拉怪机制 1.1行为树黑板 1.2获取施加伤害对象&#xff08;伤害源&#xff09; 2.修复原视频中&#xff0c;第二次攻击怪物后&#xff0c;怪物卡在原地不动的BUG 3.怪物攻击玩…

大数据新视界 -- 大数据大厂之 Impala 性能飞跃:动态分区调整的策略与方法(上)(21 / 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Vue3 笔记 (万字速通)

此笔记来至于尚硅谷&#xff0c;仅供笔者复习使用 1. Vue3 简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n&#xff09; 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Rele…

Linux基础1

Linux基础1 Linux基础1学习笔记 ‍ 声明&#xff01; ​​​学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章 笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他…

Linux中.NET读取excel组件,不会出现The type initializer for ‘Gdip‘ threw an exception异常

组件&#xff0c;可通过nuget安装&#xff0c;直接搜名字&#xff1a; ExcelDataReader using ConsoleAppReadFileData.Model; using ExcelDataReader; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Task…

320页PDF | 集团IT蓝图总体规划报告-德勤(限免下载)

一、前言 这份报告是集团IT蓝图总体规划报告-德勤。在报告中详细阐述了德勤为某集团制定的全面IT蓝图总体规划&#xff0c;包括了集团信息化目标蓝图、IT应用规划、数据规划、IT集成架构、IT基础设施规划以及IT治理体系规划等关键领域&#xff0c;旨在为集团未来的信息化发展提…

乐维网管平台(六):如何正确管理设备端口

一、什么是端口下联 在网络环境中&#xff0c;端口下联是指网络设备&#xff08;通常是交换机&#xff09;的端口与其他设备相连接的一种网络架构关系。交换机作为网络中的核心连接设备&#xff0c;其端口下联可以连接多种类型的终端设备&#xff0c;如计算机、服务器、IP 电话…

迁移学习相关基础

迁移学习 目标 将某个领域或任务上学习到的知识或模式应用到不同但相关的领域或问题中。 主要思想 从相关领域中迁移标注数据或者知识结构、完成或改进目标领域或任务的学习效果。 概述 Target data&#xff1a;和你的任务有直接关系的数据&#xff0c;但数据量少&#xff…

Diffusion Policy——斯坦福机器人UMI所用的扩散策略:从原理到其编码实现(含Diff-Control、ControlNet详解)

前言 本文一开始是属于此文《UMI——斯坦福刷盘机器人&#xff1a;从手持夹持器到动作预测Diffusion Policy(含代码解读)》的第三部分&#xff0c;考虑后Diffusion Policy的重要性很高&#xff0c;加之后续还有一系列基于其的改进工作 故独立成本文&#xff0c;且写的过程中 …

麒麟V10,arm64,离线安装docker和docker-compose

文章目录 一、下载1.1 docker1.2 docker-compose1.3 docker.service 二、安装三、验证安装成功3.1 docker3.2 docker-compose 需要在离线环境的系统了里面安装docker。目前国产化主推的是麒麟os和鲲鹏的cpu&#xff0c;这块的教程还比较少&#xff0c;记录一下。 # cat /etc/ky…

接口测试整体框架

接口测试 1. 接口 接口&#xff0c;也叫api&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;&#xff0c;接口&#xff08;Interface&#xff09;是指不同软件组件或系统之间进行交互的点。接口定义了组件之间如何通信&#xff0c;包括…

2024 ECCV | DualDn: 通过可微ISP进行双域去噪

文章标题&#xff1a;《DualDn: Dual-domain Denoising via Differentiable ISP》 论文链接&#xff1a; DualDn 代码链接&#xff1a; https://openimaginglab.github.io/DualDn/ 本文收录于2024ECCV&#xff0c;是上海AI Lab、浙江大学、香港中文大学&#xff08;薛天帆等…

AI制作ppt

1&#xff0c;kimi&#xff1a; 实际上也是AiPPT.cn这个网站&#xff08;但是有实际次数限制&#xff09; 2&#xff0c;其余专业AI ppt生成网站&#xff1a; &#xff08;1&#xff09;gamma&#xff1a;https://gamma.app/ 大概能制作7~10页左右 free的ppt&#xff0c;其余要…

10款PDF翻译工具的探索之旅:我的使用经历与工具特色!!

在如今的时代&#xff0c;PDF文件已经成为我们工作、学习和生活中不可或缺的一部分。但是&#xff0c;当遇到一些非母语或陌生语言的PDF文档时&#xff0c;这要怎么办呀&#xff01;这时候翻译工具就显得尤为重要了。这也是我所遇到过的难题&#xff0c;现在我将与大家分享几款…

【java】java通过s3访问ceph报错

1.报错信息、背景 工作中起了几个访问ceph的服务pod节点&#xff0c;一段时间后1个节点一直报错Unable to execute HTTP request: Timeout waiting for connection from pool&#xff0c;详细i信息如下图片&#xff0c;有且仅有1个节点报错&#xff0c;其他节点访问正常。看日志…

飞牛私有云访问外网

飞牛私有云 fnOS NAS 是一款有着卓越的性能以及强大的兼容性和智能化的管理界面&#xff0c;它之所以能在 NAS 市场中脱颖而出&#xff0c;是因为 fnOS 基于最新的 Linux 内核&#xff08;Debian发行版&#xff09;深度开发&#xff0c;不仅兼容主流 x86 硬件&#xff0c;还支持…

【iOS】知乎日报第三周总结

【iOS】知乎日报第三周总结 文章目录 【iOS】知乎日报第三周总结前言评论区文字评论区的一个展开效果评论区数据的一个请求修改了主页获取数据的逻辑主页无限轮播图图片主色调的一个获取将一些拓展部分的内容写在分类里小结 前言 本周笔者因为金工实习整个项目进展比较慢&#…