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

news2024/11/28 20:38:51

本系列博客主要记录一对一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。
下图显示了网络协商打印情况:
在这里插入图片描述

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

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

相关文章

燃料电池发电系统详解

目录 前言 组成结构 系统参数 常见问题 参考资料 前言 见《氢燃料电池技术综述》 见《燃料电池工作原理详解》 组成结构 燃料电池发电系统&#xff0c;由多个子系统和子模块组成&#xff0c;示例如下&#xff1a; 燃料处理系统&#xff08;fuel processing system&#xf…

开启短剧新纪元,短剧小程序开发等你来探索

一、引言 在信息爆炸的时代&#xff0c;短视频已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;随着人们对内容品质要求的不断提高&#xff0c;短剧作为一种新兴的内容形式&#xff0c;正逐渐崭露头角。为了满足广大用户对短剧内容的需求&#xff0c;我们倾力打造了…

Dark Reader:夜间模式,启动!

名人说&#xff1a;一点浩然气&#xff0c;千里快哉风。 ——苏轼 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、介绍二、下载安装1、Chrome应用商店&#xff08;需科学&#xff09;2、第三方直链下载 三、使…

在代码开发中的O(VO,BO,PO,DO,DTO)介绍

先来看个图各种O的地位&#xff1a; 从图中可以看到各种O 的位置和用法&#xff0c;接下来&#xff0c;进行简单对O进行介绍&#xff1a; BO&#xff1a;Business Object 业务对象&#xff0c;PO是一条交易记录&#xff0c;BO是一个人全部的交易记录集合对象 DTO&#xff1a;…

NSSCTF | [SWPUCTF 2021 新生赛]easy_sql

打开题目&#xff0c;提示输入一些东西&#xff0c;很显眼的可以看到网站标题为“参数是wllm” 首先单引号判断闭合方式 ?wllm1 报错了&#xff0c;可以判断为单引号闭合。 然后判断字节数&#xff08;注意‘--’后面的空格&#xff09; ?wllm1 order by 3-- 接着输入4就…

MQTT客户端软件MQTT.fx

mqtt.fx支持通过 Topic 订阅和发布消息&#xff0c;用来前期和物联网云平台调试非常方便。 在云端创建产品后&#xff0c;一般先测试一下对接接口是否可以正常使用&#xff01;该工具目前可以对接百度云、华为云&#xff0c;以及自己使用EMQ-X搭建的Mqtt服务器。 下载安装 链…

[蓝桥杯]真题讲解:抓娃娃(思维+二分)

[蓝桥杯]真题讲解&#xff1a;抓娃娃&#xff08;思维二分&#xff09; 一、视频讲解二、正解代码1、C2、python33、Java 一、视频讲解 [蓝桥杯]真题讲解&#xff1a;抓娃娃&#xff08;思维二分&#xff09;&#xff09; 二、正解代码 1、C #include<bits/stdc.h> #d…

SpringBoot 具体是做什么的?

Spring Boot是一个用于构建独立的、生产级别的、基于Spring框架的应用程序的开源框架。它的目标是简化Spring应用程序的开发和部署过程&#xff0c;通过提供一种快速、便捷的方式来创建Spring应用程序&#xff0c;同时保持Spring的灵活性和强大特性。 1. 简化Spring应用程序开…

【算法刨析】完全背包

完全背包与01背包的区别 01背包对于一个物品只能选择一次&#xff0c;但是完全背包可以选择任意次&#xff1b; 思路 和01背包类似&#xff0c;01背包我们只需要判断选或不选&#xff0c;完全背包也是如此&#xff0c;不同的是&#xff0c;对于这个物品我们在判断选后在增加一…

系统思考—愿景领导

“如果你总是低着头&#xff0c;那么你永远无法看见彩虹。”这句话不仅是对个人的提醒&#xff0c;也极其适用于每一位企业创始人。每位创始人背后不仅有一个梦想&#xff0c;还有一段充满爱的故事。 在作为策略顾问的角色中&#xff0c;我深知聆听的重要性——需要全神贯注地…

LabVIEW的MEMS电容式压力传感器测试系统

LabVIEW的MEMS电容式压力传感器测试系统 针对传统微惯性测量单元(MIMU)标定方法存在的过程繁琐、标定周期长及设备复杂等问题&#xff0c;提出了一种基于LabVIEW软件的MIMU误差参数快速标定方法。通过软件上位机控制小型三轴转台&#xff0c;配合卡尔曼滤波器技术&#xff0c;…

nginx开启局域网https访问

需求 调试webRTC 功能,需要在局域网搭建https发给我协议; 实现 环境 局域网已部署有nginx; 部署可参考专栏文章 已安装Openssl 未安装可以执行 sudo yum install openssl 这个命令进行安装 生成证书和私钥 生成私钥:打开命令提示符或终端窗口,执行以下命令以生成私钥文件…

android基础-服务

同样使用intent来传递服务 oncreate是服务第一次启动调用&#xff0c;onStartCommand是服务每次启动的时候调用&#xff0c;也就是说服务只要启动后就不会调用oncreate方法了。可以在myservice中的任何位置调用stopself方法让服务停止下来。 服务生命周期 前台服务类似于通知会…

jsp 实验12 servlet

一、实验目的 掌握怎样在JSP中使用javabean 二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握servlet的用法。【参考课本 上机实验1 】 三、源代码以及执行结果截图&#xff1a; 源代碼&#xff1a; inputVertex.jsp&#xff1a; <% page lang…

【Python深度学习(第二版)(3)】初识神经网络之深度学习hello world

文章目录 一. 训练Keras中的MNIST数据集二. 工作流程1. 构建神经网络2. 准备图像数据3. 训练模型4. 利用模型进行预测5. (新数据上)评估模型精度 本节将首先给出一个神经网络示例&#xff0c;引出如下概念。了解完本节后&#xff0c;可以对神经网络在代码上的实现有一个整体的了…

springcloud整合网关(springcloud-gateway)

pom引入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- 服务注册 --><dependency><groupId>com.alibaba.cloud</groupId&…

ALV 排序、汇总

目录 前言 实战 汇总 分类汇总 排序 分类汇总分隔方式&#xff08;仅适用于LIST ALV&#xff09; 完整代码&#xff1a; 前言 在SAP ABAP ALV中&#xff0c;排序和汇总是两个关键特性&#xff0c;用于组织和分析数据显示。 排序 排序功能允许用户根据一个或多个…

Redis是单线程吗?为什么6.0之后引入了多线程?

Redis是单线程吗&#xff1f;为什么6.0之后引入了多线程&#xff1f; Redis 是单线程吗&#xff1f;Redis 单线程模式是怎样的&#xff1f;Redis 采用单线程为什么还这么快&#xff1f;Redis 6.0 之前为什么使用单线程&#xff1f;Redis 6.0 之后为什么引入了多线程&#xff1f…

从Apache HttpClient类库,说一说springboot应用程序中的AutoConfiguration的封装

一、背景 在使用httpclient框架请求http接口的时候&#xff0c;我们往往会需要自定义配置httpclient&#xff0c;而非直接使用。 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>…

Language2Pose: Natural Language Grounded Pose Forecasting # 论文阅读

URL https://arxiv.org/pdf/1907.01108 TD;DR 19 年 7 月 cmu 的文章&#xff0c;提出一种基于 natural language 生成 3D 动作序列的方法。通过一个简单的 CNN 模型应该就可以实现 Model & Method 首先定义一下任务&#xff1a; 输入&#xff1a;用户的自然语言&…