一对一WebRTC视频通话系列(五)——综合调试和功能完善

本系列博客主要记录一对一WebRTC视频通话实现过程中的一些重点,代码全部进行了注释,便于理解WebRTC整体实现。


本专栏知识点是通过<零声教育>的音视频流媒体高级开发课程进行系统学习,梳理总结后写下文章,对音视频相关内容感兴趣的读者,可以点击观看课程网址:零声教育


一对一WebRTC视频通话系列往期博客

一对一WebRTC视频通话系列(一)—— 创建页面并显示摄像头画面
一对一WebRTC视频通话系列(二)——websocket和join信令实现
一对一WebRTC视频通话系列(三)——leave和peer-leave信令实现
一对一WebRTC视频通话系列(四)——offer、answer、candidate信令实现


主要完成工作:
(1)点击离开时,要将RTCPeerConnection关闭(close);
(2)点击离开时,要将本地摄像头和麦克风关闭;
(3)检测到客户端退出时,服务器再次检测该客户端是否已经退出房间。
(4)RTCPeerConnection时传入ICE server的参数,以便当在公网环境下可以进行正常通话

一、客户端

(1)点击离开时,要将RTCPeerConnection关闭(close);
(2)点击离开时,要将本地摄像头和麦克风关闭;
点击离开按钮后,
doLeave() -> hangup() -> closeLocalStream()

  1. doLeave()函数:发送消息离开房间。
    首先,创建一个jsonMsg对象,包含命令(cmd)、房间ID(roomId)和本地用户ID(localUserId)。
    使用JSON.stringify()方法将jsonMsg对象转换为字符串。
    调用zeroRTCEngine.sendMessage()方法将消息发送给服务器。
    输出消息内容到控制台。
    调用hangup()函数关闭本地视频流和RTCPeerConnection

  2. hangup()函数:关闭本地视频流和RTCPeerConnection
    设置localVideo.srcObject为null,不显示本地流。
    设置remoteVideo.srcObject为null,不显示对方。
    调用closeLocalStream()函数关闭本地流。
    检查pc变量是否为null,如果是,则关闭RTCPeerConnection

  3. closeLocalStream()函数:关闭本地视频流。
    检查localStream变量是否为null,如果是,则停止所有视频流。

  4. openLocalStream(stream)函数:用于打开本地流。
    检查stream变量是否为null,如果是,则输出日志信息,并调用doJoin()函数

function doLeave() {
    var jsonMsg = {
        'cmd': 'leave',
        'roomId': roomId,
        'uid': localUserId,
    };
    var message = JSON.stringify(jsonMsg); //将json对象转换为字符串
    zeroRTCEngine.sendMessage(message);   //设计方法:用实现方法而不是直接用变量    
    console.info("doLeave message: " + message);
    hangup();
}

function hangup() {
    localVideo.srcObject = null;//0.不显示本地流
    remoteVideo.srcObject = null;//1.不显示对方
    closeLocalStream();//2.关闭本地流
    if(pc != null){
        pc.close();//3.关闭RTCPeerConnection
        pc = null;
    }
}
function closeLocalStream() {
    if(localStream != null){
        localStream.getTracks().forEach(function(track){
            track.stop();
        });
        localStream = null;
    }
}

关闭远程视频流,关闭RTCPeerConnection对象。

function handleRemotePeerLeave(message) {
    console.info("handleRomotePeerLeave message: " + message.remoteUid);
    remoteVideo.srcObject = null;
    if(pc != null){
        pc.close();//关闭RTCPeerConnection
        pc = null;
    }
}

二、服务端

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

var server = ws.createServer(function (conn) {
    console.log("New connection");
    conn.sendText('我收到你的连接了')
    conn.client = null;//1.对应客户端信息
    // 监听客户端发送的消息
    conn.on("text", function (str) {
        console.info("Received msg:"+str);
        var jsonMsg = JSON.parse(str);

        switch(jsonMsg.cmd){
            case SIGNAL_TYPE_JOIN:
                conn.client = handleJoin(jsonMsg, conn); //2.返回值需要修改
                break;
            case SIGNAL_TYPE_LEAVE:
                handleLeave(jsonMsg);
                break;
            case SIGNAL_TYPE_OFFER:
                handleOffer(jsonMsg);
                break;
            case SIGNAL_TYPE_ANSWER:
                handleAnswer(jsonMsg);
                break;                 
            case SIGNAL_TYPE_CANDIDATE:
                handleCandidate(jsonMsg);
            break;
        }
    });

    // 连接关闭时的输出
    conn.on("close", function (code, reason) {
        console.log("Connection closed,code: " + code + " reason:" + reason); 
        if(conn.client != null){ //3.如果客户还未删除
            //强制让客户端从房间退出
            handleForceLeave(conn.client);
        }
    });
    // 监听连接错误
    conn.on("error", function (error) {
        console.error("发生错误:", error);
    })
}).listen(port);

当连接关闭时,如果连接的客户端对象尚未删除,调用handleForceLeave函数强制让客户端从房间退出。

实现原理:
通过监听用户连接对象的 client.onclose 事件来触发 handleForceLeave 函数,并在其中强制离开用户。
1.首先,从用户连接对象中获取房间ID和用户ID。
2.然后,在全局变量roomTableMap 中查找房间ID对应的房间对象。
3.判断用户ID是否存在于房间内。如果不在房间内,则输出日志并返回。
4.如果用户已经在房间内,则输出日志并从房间对象中移除用户ID。
5.判断房间对象中是否还有其他用户。如果有其他用户,则给所有房间内其他用户发送用户离开消息。

function handleForceLeave(client){
   // 获取房间ID和用户ID
   var roomId = client.roomId;
   var uid = client.uid;

   // 1.先查找房间号
   var roomMap = roomTableMap.get(roomId);
   if(roomMap == null){
        console.error("roomId: " + roomId + " is not exist");
        return;
   }
   // 2.判别uid是否在房间内
   if(roomMap.contains(uid) == false)  {
        console.info("uid: " + uid + " have leave roomId " + roomId);
        return;
   }

   // 3.说明客户端没有正常离开,所以我们要强制离开程序
   console.info("uid: " + uid + " force leave roomId " + roomId);

   roomMap.remove(uid);
   if(roomMap.size() >= 1){
       var clients = roomMap.getEntrys();
       for(var i in clients){
           jsonMsg = {
               'cmd':SIGNAL_TYPE_PEER_LEAVE,
               'remoteUid':uid
           }
           // 给所有房间内其他用户发送用户离开消息
           var msg = JSON.stringify(jsonMsg);
           var remoteUid = clients[i].key;
           var remoteClient =roomMap.get(remoteUid);
           if(remoteClient){
               console.info("notify peer "+ remoteClient.uid +" uid " + uid + " leave room");
               remoteClient.conn.sendText(msg);
           }
       }
   }    
}

三、公网通讯

(4)RTCPeerConnection时传入ICE server的参数,以便当在公网环境下可以进行正常通话

3.1 启动coturn

# nohup是重定向命令,输出都将附加到当前目录的 nohup.out 文件中; 命令后加 & ,后台执行起来后按
ctr+c,不会停止
sudo nohup turnserver -L 0.0.0.0 -a -u sxl:zxc -v -f -r nort.gov &
# 前台启动
sudo turnserver -L 0.0.0.0 ‐a -u sxl:zxc -v -f -r nort.gov
#然后查看相应的端口号3478是否存在进程
sudo lsof -i:3478

安装 sysstat包,查看网络情况

sudo apt-get install sysstat
sudo sar -n DEV 1

3.2修改配置文件

修改客户端main.js代码,

    var defaultConfiguration = {  
        bundlePolicy: "max-bundle",
        rtcpMuxPolicy: "require",
        iceTransportPolicy:"relay",//relay 或者 all
        // 修改ice数组测试效果,需要进行封装
        iceServers: [
            {
                "urls": [
                    "turn:192.168.226.3:3478?transport=udp",
                    "turn:192.168.226.3:3478?transport=tcp"       // 可以插入多个进行备选
                ],
                "username": "sxl",
                "credential": "zxc"
            },
            {
                "urls": [
                    "stun:192.168.226.3:3478"
                ]
            }
        ]
    };

new RTCPeerConnection()时,使用defaultConfiguration参数来创建对象。

pc = new RTCPeerConnection(defaultConfiguration);

测试

设置configuration,先设置为relay中继模式,只有relay中继模式可用的时候,才能部署到公网去(部署到公网后也先测试relay)。
使用realy中继:
在这里插入图片描述
使用局域网P2P
在这里插入图片描述
可以看出采用realy中继通信下,网络流量明显大于p2p。
下图显示了网络协商打印情况:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

猿匹配,一款使用环信实现的一个开源聊天应用含服务器

前言 之前写了一篇Android开发集成聊天环信SDK3.x简单开始&#xff0c;然后最近得空开发了一款使用环信实现的实时聊天应用&#xff0c;包含简单的服务器端&#xff0c;并开源给大家&#xff0c;有兴趣的同学可以一起搞一下&#xff0c;详细介绍看下边吧 上代码 服务器&#…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《计及全生命周期成本的公交光伏充电站储能优化配置方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

清华团队开发首个AI医院小镇模拟系统;阿里云发布通义千问 2.5:超越GPT-4能力;Mistral AI估值飙升至60亿美元

&#x1f989; AI新闻 &#x1f680; 清华团队开发首个AI医院小镇模拟系统 摘要&#xff1a;来自清华的研究团队最近开发出了一种创新的模拟系统&#xff0c;名为"Agent Hospital"&#xff0c;该系统能够完全模拟医患看病的全流程&#xff0c;其中包括分诊、挂号、…

【八十五】【算法分析与设计】单调栈的全新版本,两个循环维护左小于和右小于信息,84. 柱状图中最大的矩形,85. 最大矩形

84. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1,5,6,2,3] 输出&#xff1a;10 解释&am…

Go的安装与配置

安装 https://go.dev/dl/ 以Windows上安装为例&#xff1a; 下一步下一步&#xff0c;记住安装位置 安装完成后 win rcmd 键入go version检查是否安装成功 配置 Go 工作区 Go 在组织项目文件方面与其他编程语言不同。 Go 是在工作区的概念下工作的。但是从版本 1.11 开始&…

docker-compose部署java项目

docker-compose是定义和运行多容器的工具。换句话说就是通过配置yml文件来运行容器&#xff0c;简化了每次输入docker run等命令&#xff0c;把这些命令配置在yml文件统一管理&#xff0c;而且可以用一个yml文件一次启动多个容器&#xff0c;启动时还可以设置各个容器的依赖关系…

远程开机与远程唤醒BIOS设置

远程开机与远程唤醒BIOS设置 在现代计算机应用中&#xff0c;远程管理和控制已成为许多企业和个人的基本需求。其中&#xff0c;远程开机和远程唤醒是两项非常实用的功能。要实现这些功能&#xff0c;通常需要在计算机的BIOS中进行一些特定的设置。以下是对远程开机和远程唤醒…

如何判断nat网络?如何内网穿透

大家都清楚&#xff0c;如果你想开车&#xff0c;就必须要给车上一个牌照&#xff0c;随着车辆越来越多&#xff0c;为了缓解拥堵&#xff0c;就需要摇号&#xff0c;随着摇号的人数越来越多&#xff0c;车牌对于想开车的人来说已经成为奢望。在如今的IPv4时代&#xff0c;我们…

全自动减压器法二氧化碳气容量测试仪:饮料行业的革新与未来

全自动减压器法二氧化碳气容量测试仪&#xff1a;饮料行业的革新与未来 一、引言 在追求品质与效率的现代饮料生产领域&#xff0c;全自动减压器法二氧化碳气容量测试仪凭借其高精度、高效率及无人工干预的显著优势&#xff0c;正逐渐成为行业的标杆。特别是在碳酸饮料的生产中…

USB系列五:USB设备配置(重要)

在USB总线接口协议中&#xff0c;对于USB外部设备功能特征是通过端点&#xff08;Endpoint&#xff09;、配置&#xff08;Configuration&#xff09;和接口&#xff08;Interface&#xff09;来描述的&#xff0c;这些就是典型的USB描述符。 USB主机通过设备请求来读取外部设…

并行执行线程资源管理方式——《OceanBase 并行执行》系列 3

在某些特定场景下&#xff0c;由于需要等待线程资源&#xff0c;并行查询会遇到排队等待的情况。本篇博客将介绍如何管理并行执行线程资源&#xff0c;以解决这种问题。 《OceanBase并行执行》系列的内容分为七篇博客&#xff0c;本篇是其中的第三篇。前2篇如下&#xff1a; 一…

分布式与一致性协议之Quorum NWR算法

Quorum NWR算法 概述 不知道你在工作中有没有遇到过这样的事情:你开发实现了一套AP型分布式系统&#xff0c;实现了最终一致性&#xff0c;且业务接入后运行正常&#xff0c;一切看起来都那么美好。 可是突然有同事说&#xff0c;我们要拉这几个业务的数据做实时分析&#xf…

AXI4读时序在AXI Block RAM (BRAM) IP核中的应用

在本文中将展示描述了AXI从设备&#xff08;slave&#xff09;AXI BRAM Controller IP核与Xilinx AXI Interconnect之间的读时序关系。 1 Single Read 图1展示了一个从32位BRAM&#xff08;Block RAM&#xff09;进行AXI单次读取操作的时序示例。 图1 AXI 单次读时序图 在该…

webpack如何自定义一个loader

我们在使用脚手架的搭建项目的时候往往都会帮我们配置好所需的loader&#xff0c;接下来讲一下我们要如何自己写一个loader应用到项目中&#xff08;完整代码在最后&#xff09; 1. 首先搭建一个项目并找到webpack配置文件&#xff08;webpack.config.js&#xff09; 在modul…

clickhouse学习笔记06

ClickHouse的建表和引擎选择思路讲解 ClickHouse的常见注意事项和异常问题排查 ClickHouse高性能查询原因剖析-稀疏索引 ClickHouse高性能写入剖析-LSM-Tree存储结构

嵌入式开发十:STM32开发基础入门知识补充

本篇博客主要是针对前面STM32入门基础知识的补充&#xff0c;为后面的真正开发学习做好准备。 目录 一、IO 引脚复用器和映射 1.1 引脚复用的概念 1.2 如何设计实现复用 1.3 复用功能固件库配置过程 二、STM32 NVIC 中断优先级管理 2.1 NVIC中断优先级管理结构体介绍 …

力扣每日一题-统计已测试设备-2024.5.10

力扣题目&#xff1a;统计已测试设备 题目链接: 2960.统计已测试设备 题目描述 代码思路 根据题目内容&#xff0c;第一感是根据题目模拟整个过程&#xff0c;在每一步中修改所有设备的电量百分比。但稍加思索&#xff0c;发现可以利用已测试设备的数量作为需要减少的设备电…

16地标准化企业申请!安徽省工业和信息化领域标准化示范企业申报条件

安徽省工业和信息化领域标准化示范企业申报条件有哪些&#xff1f;合肥市 、黄山市 、芜湖市、马鞍山、安庆市、淮南市、阜阳市、淮北市、铜陵市、亳州市、宣城市、蚌埠市、六安市 、滁州市 、池州市、宿州市企业申报安徽省工业和信息化领域标准化示范企业有不明白的可在下文了…

机器学习各个算法的优缺点!(上篇) 建议收藏。

下篇地址&#xff1a;机器学习各个算法的优缺点&#xff01;&#xff08;下篇&#xff09; 建议收藏。-CSDN博客 今天有朋友聊起来&#xff0c;机器学习算法繁多&#xff0c;各个算法有各个算法的特点。 以及在不同场景下&#xff0c;不同算法模型能够发挥各自的优点。 今天…

Java之异常处理

一、认识异常 1.异常的概念 在 Java 中&#xff0c;将程序执行过程中发生的不正常行为称为异常。。比如之前写代码时经常遇到的&#xff1a; 1. 算术异常 System.out.println(10 / 0); // 执行结果 Exception in thread "main" java.lang.ArithmeticException: /…