web端视频通话sdk集成和功能实现流程(实时视频系列五)

article/2025/10/13 6:08:28

本文介绍的是在wed端如何集成即构音视频SDK,以实现在wed端一对一,一对多及多人实时视频通话的功能。

一、SDK集成指引

1、安装

请从 jZego-RTC-SDK 下载 SDK。 npm下载包支持typescript语言(推荐):

npm i webrtc-zego

2、集成SDK

可使用script直接引入

<script src="jZego-rtc-1.0.1.js"></script>

或者

import {ZegoClient} from 'webrtc-zego'var ZegoClient = require('webrtc-zego').ZegoClient

API 说明请参考:Web SDK API 说明。

3、兼容性说明

由于不同的浏览器对于 Web 的支持各不相同,目前 SDK 支持的浏览器的版本如下:
这里写图片描述

二、功能实现流程

1、实现流程

以下是简单的SDK调用流程,详细API接口请参考接口文档

1.1 初始化SDK

// 声明变量
var zg;// 初始化实例
zg = new ZegoClient();// 配置必要参数
zg.config({appid: appid,         // 必填,应用id,由即构分配idName: idName,       // 必填,用户自定义id,全局唯一nickName:  nickName,  // 必填,用户自定义昵称remoteLogLevel: 1,     // 上传日志最低级别,建议跟 logLevel 一致logLevel: 1,          // 日志级别,debug:0,info:1,warn:2,error:3,report:99,disable:100(数字越大,日志越少),建议选择 1server: server        // 必填,接入服务器地址,由即构分配logUrl: logUrl        // 必填,logServer 地址,由即构分配
});

1.2 获取登录 token

登录 token 的获取详见后文 3 安全方案 中的 3.1 房间登录安全。

源码片段如下:

// 获取登录 token
function loadLoginToken() {var xmlhttp;xmlhttp = new XMLHttpRequest();xmlhttp.onreadystatechange = function() {if (xmlhttp.readyState == 4) {if (xmlhttp.status == 200) {trace("login token success:" + xmlhttp.responseText);loginToken = xmlhttp.responseText;doLogin();}else {trace("login token failed");alert("获取登录信息失败");}}};//从开发者后台获取tokenxmlhttp.open("GET", loginTokenUrl + "?app_id=" + appid + "&id_name=" + idName, true);xmlhttp.send();}

1.3 登录房间

登录房间成功是后续信令操作的前提。源码片段如下,仅供参考:

zg.login(roomId, 2, loginToken, function(streamList) {//登录成功处理逻辑
}, function(err) {alert("login error: " + err.msg);
});

1.4 枚举设备

SDK提供接口可以枚举当前的麦克风,摄像头,扬声器设备。源代码片段如下,仅供参考:

zg.enumDevices(function(deviceInfo) {if (deviceInfo.microphones) {for (var i = 0; i < deviceInfo.microphones.length; i++) {trace("microphone: " + deviceInfo.microphones[i].label);}}if (deviceInfo.cameras) {for (var j = 0; j < deviceInfo.cameras.length; j++) {trace("camera: " + deviceInfo.cameras[i].label);}}});

1.5 本地预览

在开播前可以预览当前画面,可以指定麦克风和摄像头的设备Id,也可以使用默认设备。源代码片段如下,仅供参考:

function doPreview(audioInput, videoInput) {var avConstraints = {audio: true,audioInput: audioInput,video: true,videoInput: videoInput,videoQuality: videoQuality,horizontal: true};//WebRTC可以共用相同设备,所以预览时必须指定开播的streamIdzg.startPreview( localVideo, avConstraints, function() {trace("preview success");doPublish();}, function(error) {alert("start device error " , error);});
}
  • 使用默认设备时,audioInput和videoInput参数不用填

    推纯音频或纯视频时,只需将对应的video或audio设为false,然后在播放拉流接口中传入第四个播放模式参数,例:zg.startPlayingStream(streamId,
    video,null,{playType:’audio’});

1.6 开始直播

开始直播前必须先开始预览。源代码片段如下,仅供参考:

function doPublish() {var result = zg.startPublishingStream(publishStreamId, localVideo);if (!result) {alert("publish " + publishStreamId + " return " + result);}
}

1.7 直播状态回调

直播开始,直播失败,重试直播都通过此回调通知。源代码如下,仅供参考:

zg.onPublishStateUpdate = function(type, streamid, error) {if (type == 0) {trace("publish " + streamid + " success");}else if (type == 2) {trace("publish " + streamid + " retry");}else {// trace("publish " + streamid + "error " + error.code);alert("publish " + streamid + " error " + error.msg);}
};

1.8 开始拉流

指定播放流的video标签,画面会渲染在指定的video标签里。源代码如下,仅供参考:

function doPlay(streamid) {trace("play " + streamid);var remoteVideo = document.createElement("video");remoteVideo.setAttribute("autoplay", "");remoteVideo.setAttribute("webkit-playsinline", "");var videos = util.getById("videos");videos.appendChild(remoteVideo);remoteVideoMap[streamid] = remoteVideo;var result = zg.startPlayingStream(streamid, remoteVideo);if (!result) {alert("play " + streamid + " return " + result);}
}

1.9 拉流状态回调

拉流开始,拉流失败,拉流重试都通过此回调通知。源代码如下,仅供参考:

zg.onPlayStateUpdate = function(type, streamid, error) {if (type == 0) {trace("play " + streamid + " success");}else if (type == 2) {trace("play " + streamid + " retry");}else {// trace("publish " + streamid + "error " + error.code);alert("play " + streamid + " error " + error.msg);}
};

2、屏幕共享

2.1 使用说明

  • web屏幕共享功能,目前只支持桌面端(例如window和mac)的chrome和火狐浏览器,其中chrome需要下载即构共享插件

    分享功能只能获取到系统扬声器声音,外部麦克风需另外推流(栗子:电脑播放音乐可以采集到,对着电脑说话声音采集不到)

    当同时推多路流时,建议音频只推一路,防止回音

2.2 插件安装

  • 解压即构共享插件;

    打开你的 Chrome 浏览器,点击屏幕右上方的扩展按钮,选择 更多工具 > 扩展程序, 打开开发者模式 > 加载已解压的扩展程序 >
    选择 解压的 即构共享插件文件夹,即可完成安装。

2.3 方法调用
这里写图片描述
2.4推流示例(chrome)

   zg.startScreenShotChrome(function (suc,mediastream) {previewVideo.srcObject = mediastream;//本地预览// 与正常推流一样,需要先调用sdk预览接口,成功后再推流 ,// 需要注意的是  mediaStreamConstraints.externalCapture = true (必须)          zg.startPreview(localVideo, mediaStreamConstraints, function() {zg.startPublishingStream(streamid, localVideo, extraInfo);}, error)})

3 安全方案

3.1 房间登录安全

3.1.1 基本流程

  • JS与业务后台建立通信,获取 Token 信息。

    JS调用 ZegoClient.login 登录 Zego 服务器,传入 Token 信息,验证通过后,完成登录。

    ZegoClient 会保持与 Zego 服务器的长连接,处理发送或接收的消息。

    JS调用 ZegoClient.logout 登出 Zego 服务器。

请注意:
生成 Token 信息需要业务后台自行开发。

3.1.2 login_token 信息

login_token 信息为标准 json 格式,具体为:

{"ver": 1,"hash": xxxxx,"nonce": xxxxx,"expired": xxxxx,
}

这里写图片描述

请注意:

login_token 传输过程中,会经过 base64 加密。

每次登录都要重新获取 login_token。

业务方开发的JS需要和业务后台建立一种安全通讯和鉴权机制,业务方使用自有的账户体系或第三方认证体系的登录完成后,业务方JS和业务后台交互获取该
login_token, AppSecret 是存储在业务后台的。

3.2 login_token 生成示例代码

go 语言 login_token 生成示例代码如下:

func makeTokenSample(appid uint32, app_key string, idname string, expired_add int64) (ret string, err error){nonce := UniqueId()expired := time.Now().Unix() + expired_add      //单位:秒app_key = strings.Replace(app_key, "0x","",-1)app_key = strings.Replace(app_key, ",", "", -1)if len(app_key) < 32 {return "", fmt.Errorf("app_key wrong")}app_key_32 := app_key[0:32]source := fmt.Sprintf("%d%s%s%s%d",appid,app_key_32,idname,nonce,expired)sum := GetMd5String(source)token := tokenInfo{}token.Ver = 1token.Hash = sumtoken.Nonce = noncetoken.Expired = expiredbuf, err := json.Marshal(token)if err != nil {return "", err}encodeString := base64.StdEncoding.EncodeToString(buf)return encodeString, nil
}

php 语言 login_token 生成示例代码如下:

public function getToken(int $app_id, string $app_key, string $idname, int $expired_add)
{$nonce = uniqid();$expired = time() + $expired_add; //单位:秒$app_key = str_replace("0x", "", $app_key);$app_key = str_replace(",", "", $app_key);if(strlen($app_key) < 32) {return false;}$app_key_32 = substr($app_key, 0, 32);$source = $app_id.$app_key_32.$idname.$nonce.$expired;$sum = md5($source);$tokenInfo = ['ver' => 1,'hash'  => $sum,'nonce' => $nonce,'expired' => $expired,];$token = base64_encode(json_encode($tokenInfo));return $token;
}

java 语言 login_token 生成示例代码如下:

package demo;import org.json.JSONObject;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;public class ZegouUtils {public static void main(String[] args) {String appid = "0000000000";  //即构分配的appIdString appKey = "0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00";  //即构分配的appKeyString idName = "xxxxxxx"; //业务系统用户唯一标识String Token = getZeGouToken(appid,appKey,idName);System.out.println("--Token--:"+Token);}/*** 拉流端获取登录token* @param appId  即构分配的appId* @param appKey 即构分配的appKey* @param idName 业务系统用户唯一标识* @return*/public static String getZeGouToken(String appId,String appKey,String idName){String nonce= UUID.randomUUID().toString().replaceAll("-", "");long time=new Date().getTime()/1000+30*60;String appKey32=new String(appKey.replace("0x", "").replace(",", "").substring(0, 32));System.out.println("appKey:"+time+"    "+appKey32+"    "+nonce);if(appKey32.length()<32){System.out.println("private key erro!!!!");return null;}String sourece= getPwd(appId+appKey32+idName+nonce+time);System.out.println("hash:"+sourece);JSONObject json=new JSONObject();json.put("ver", 1);json.put("hash", sourece);json.put("nonce", nonce);json.put("expired",time); //unix时间戳,单位为秒org.apache.commons.codec.binary.Base64 base64 = new org.apache.commons.codec.binary.Base64();System.out.println("json"+json.toString());return base64.encodeAsString(json.toString().getBytes());}/*** 获取MD5加密* @param pwd 需要加密的字符串* @return String字符串 加密后的字符串*/public static String getPwd(String pwd) {try {// 创建加密对象MessageDigest digest = MessageDigest.getInstance("md5");// 调用加密对象的方法,加密的动作已经完成byte[] bs = digest.digest(pwd.getBytes());// 接下来,我们要对加密后的结果,进行优化,按照mysql的优化思路走// mysql的优化思路:// 第一步,将数据全部转换成正数:String hexString = "";for (byte b : bs) {// 第一步,将数据全部转换成正数:int temp = b & 255;// 第二步,将所有的数据转换成16进制的形式// 注意:转换的时候注意if正数>=0&&<16,那么如果使用Integer.toHexString(),可能会造成缺少位数// 因此,需要对temp进行判断if (temp < 16 && temp >= 0) {// 手动补上一个“0”hexString = hexString + "0" + Integer.toHexString(temp);} else {hexString = hexString + Integer.toHexString(temp);}}return hexString;} catch (NoSuchAlgorithmException e) {// TODO Auto-generated catch blocke.printStackTrace();}return "";}}

4、常见错误

4.1 登陆房间时,控制台输出返回ZegoClient.Error.Timeout

解决方案: 请检查 初始化配置 ZegoClient.config 里面每个参数的类型是否正确,一般情况下都是类型不正确引起的问题。
这里写图片描述

4.2 登陆房间时,控制台输出返回result=1000001002

原因: 观众角色不允许创建房间,也就是这个房间不存在。

解决方案:

1)在ZegoClient.config里面的option.audienceCreateRoom设置为TRUE

2)在ZegoClient.login里面将role设置为1,主播角色。

4.3 登陆房间时,控制台输出返回token Error的报错

原因:计算token的时候,算法不对引起,可通过这个链接:http://sig-wstoken.zego.im:8181/tokenindex 来验证是否正确:

1) 首先验证 hash 字段是否有错,这里要注意 id_name 是string类型,不是数值类型; expired 是 uninx 的时间戳,不是北京时间。

2) login_token由业务侧后台生成后,将json里面的字段 经过base64加密 后再下发给web端。


http://chatgpt.dhexx.cn/article/5cHtbUGf.shtml

相关文章

《保姆级教程》基于Agora SDK实现音视频通话及屏幕共享

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想…

保姆级教程!基于声网 Web SDK实现音视频通话及屏幕共享

前言 大家好&#xff0c;我是 小曾同学&#xff0c;小伙伴们也可以叫我小曾&#xff5e; 如果你想实现一对一音视频通话和屏幕共享功能&#xff0c;不妨来看看这篇文章&#xff0c;保姆级教程&#xff0c;不需要从零实现&#xff0c;直接集成声网 SDK 即可轻松上手。 本文也…

Android设备实现语音视频通话

一、背景 有智慧社区相关的项目&#xff0c;需要门禁开发门禁机APP以及用户端APP&#xff0c;要求实现门禁设备呼叫业主APP&#xff0c;业主接通后可以通话、可以开门。这里主要要做的就是语音和视频通话功能&#xff0c;以及远程开门。至于业务逻辑那就是正常的需求处理了。 …

基于 Web SDK 实现视频通话场景 | 声网 SDK 教程

声网视频 SDK 被广泛应用于多种实时互动场景中&#xff0c;例如视频会议、视频通话、音视频社交、在线教育等。为了让刚刚接触声网 SDK 的开发者&#xff0c;可以更顺畅地实现基础的视频通话功能&#xff0c;我们基于声网 Web SDK 4.x 版本梳理了本篇教程。 在本文末&#xff0…

如何基于 Agora Android SDK 在应用中实现视频通话?

在很多产品&#xff0c;实时视频通话已经不是新鲜的功能了&#xff0c;例如视频会议、社交应用、在线教育&#xff0c;甚至也可能出现在一些元宇宙的场景中。 本文将教你如何通过声网Agora 视频 SDK 在 Android 端实现一个视频通话应用。声网 SDK 每个月会提供 10000 分钟的免…

Android uni-app实现音视频通话

前言 上一篇讲解了怎么实现Android uni-app封装原生插件&#xff0c;这篇讲解一下&#xff0c;把anyRTC的RTC&#xff08;音视频通讯&#xff09;封装uni-app 实现音视频通话。 不了解anyRTC的小伙伴&#xff0c;可以点击下面链接&#xff1a; 开发者官网 1.效果图 先上图&a…

使用 Agora SDK 开发 React Native 视频通话 App

在 React Native 的应用中&#xff0c;从头开始添加视频通话功能是很复杂的。要保证低延迟、负载平衡&#xff0c;还要注意管理用户事件状态&#xff0c;非常繁琐。除此之外&#xff0c;还必须保证跨平台的兼容性。 当然有个简单的方法可以做到这一点。在本次的教程中&#xf…

MySQL联合索引底层数据结构是怎样的

目录 1. 联合索引数据结构图 2.联合索引是如何进行排序的 3. 联合索引查询特点 1. 联合索引数据结构图 如下图所示联合索引的数据结构, 通过name&#xff0c;age&#xff0c;position三个字典进行一个联合索引&#xff0c;构建B树索引结构。 2.联合索引是如何进行排序的 B树…

MySQL联合索引在B+树如何存储以及寻址

文章目录 引入Innodb B树联合索引存储以及寻址总结 引入 最近找工作&#xff0c; 去一家三方支付公司面试&#xff0c;前面得过程还挺好&#xff0c;所有的提都回答对了&#xff08;心里暗自窃喜应该能拿到高工资offer&#xff0c;迎娶白富美&#xff0c;然后走向人生巅峰&…

MySQL联合索引生效规则

最近项目中一张表数量测试达到千万级别&#xff0c;考虑加索引&#xff0c;对于单列索引及联合索引&#xff08;复合索引&#xff09;简单学习了下&#xff0c;做一下笔记。 联合索引生效前提&#xff1a;因为联合索引有顺序讲究&#xff0c;联合索引的第一个字段是引导列&…

mysql联合索引的数据结构

一、本文主要讲解的内容有&#xff1a; 联合索引在B树上的存储结构联合索引的查找方式为什么会有最左前缀匹配原则 在分享这篇文章之前&#xff0c;我在网上查了关于MySQL联合索引在B树上的存储结构这个问题&#xff0c;翻阅了很多博客和技术文章&#xff0c;其中有几篇讲述的…

mysql联合索引有效和失效的情况分析

关于mysql的索引&#xff0c;是mysql优化一个非常重要的方面。那么关于索引是否有效就是非常关键了。很多人设计了索引&#xff0c;但是发现依旧很慢。那么这个时候就判断sql的索引执行情况非常重要了。网上有大量的博主也写过不少类似的文章&#xff0c;但是关于联合索引的具体…

MySQL联合索引(abc)命中规则

1.建表 mysql创建一张表&#xff0c;表名&#xff1a;‘test_models’ id列为 主键&#xff0c;int类型 &#xff0c;自增a,b,c,d,e 全部是int&#xff08;11&#xff09;为&#xff08;a,b,c&#xff09;添加一个联合索引 index_abc 执行语句&#xff1a; CREATE TABLE te…

mysql联合索引

mysql联合索引的使用 命名规则&#xff1a;表名_字段名 1、需要加索引的字段&#xff0c;要在where条件中 2、数据量少的字段不需要加索引 3、如果where条件中是OR关系&#xff0c;加索引不起作用 4、符合最左原则 以下是我的建表语句 CREATE TABLE test ( id int(11) uns…

mysql 联合索引结构与索引匹配原则

联合索引结构与索引匹配原则 最左前缀匹配原则&#xff1a;在MySQL建立联合索引时会遵守最左前缀匹配原则&#xff0c;即最左优先&#xff0c;在检索数据时从联合索引的最左边开始匹配。 要想理解联合索引的最左匹配原则&#xff0c;先来理解下索引的底层原理。索引的底层是一…

详解MySQL联合索引

引言 本文预计分为两个部分:(1)联合索引部分的基础知识 在这个部分&#xff0c;我们温习一下联合索引的基础(2)联合索引部分的实战题 在这个部分&#xff0c;列举几个我认为算是实战中的代表题&#xff0c;挑出来说说。 正文 基础 讲联合索引&#xff0c;一定要扯最左匹配!…

mysql联合索引的使用

这篇笔记主要记录联合索引的使用 设置了shopId、userId、relationId三个字段&#xff0c;作为联合索引&#xff0c;这三个字段&#xff0c;都是long类型的&#xff0c;也就是bigint 分别验证以下几个场景&#xff1a; 场景一&#xff1a;explain select * from testIndex wher…

MySQL联合索引的原理

面试中被问到了联合索引&#xff0c;突然就涉及到了知识盲区&#xff0c;对不起&#xff0c;我只知道B树&#xff0c;B树&#xff0c;哈希索引&#xff0c;聚簇索引&#xff0c;非聚簇索引&#xff0c;联合索引的原理&#xff1f;。。 对不起涉及到了我的知识盲区了。 这里对联…

Mysql 联合索引

联合索引底层数据结构 MySQL可以使用多个字段同时建立一个索引,叫做联合索引。上文中讲到索引的底层结构就是一个二叉树&#xff0c;联合索引也是一样&#xff0c;它的非叶子节点中存的就不只是一个列&#xff0c;是索引的所有列&#xff0c;并且它的排序就是根据索引列的先后顺…

mysql联合索引详解

比较简单的是单列索引&#xff08;btree&#xff09;。遇到多条件查询时&#xff0c;不可避免会使用到多列索引。联合索引又叫复合索引。 btree结构如下&#xff1a; 每一个磁盘块在mysql中是一个页&#xff0c;页大小是固定的&#xff0c;mysql innodb的默认的页大小是16k&a…