webRtc 示例
1、使用socket.io进行会话
2、为了方便,参数写死在前端了,前端界面1代码如下(由界面1发起视频):
<!DOCTYPE html>
<html><head><title>Socket.IO chat</title><meta charset="utf-8"><meta name="description" content="WebRTC code samples"><meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1"><meta itemprop="description" content="Client-side WebRTC code samples"><meta itemprop="name" content="WebRTC code samples"><meta name="mobile-web-app-capable" content="yes"><meta id="theme-color" name="theme-color" content="#ffffff"><!-- 引入2.0版本的socket.io文件 --><script src="socket.io.js"></script><script src="webrtc.js"></script><script src="jquery-3.6.4.js"></script><link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css"><link rel="stylesheet" href="css/main.css" /><link rel="stylesheet" href="css/c1/main.css" /></head><body><div id="container"><video id="localVideo" title="a" playsinline autoplay muted></video><video id="remoteVideo" title="b" playsinline autoplay></video><div><button id="prepareBtn" type="button" onclick="prepare()">准备</button><button id="callBtn" type="button" onclick="callVideo()">呼叫</button><button id="cancelBtn" type="button" onclick="cancelVideo()">挂断</button></div></div><script>const prepareButton = document.getElementById('prepareBtn');const callBtn = document.getElementById('callBtn');const cancelBtn = document.getElementById('cancelBtn');const localVideo = document.getElementById('localVideo');const remoteVideo = document.getElementById('remoteVideo');let localStream;let pc1;let pc2;let to = '2';var iceServer = {{// urls: 'stun:stun.l.google.com:19302'urls: 'stun:stun.services.mozilla.com'}]};const offerOptions = {offerToReceiveAudio: 1,offerToReceiveVideo: 0};//建立socket连接const socket = io("https://192.168.3.21", {reconnectionDelayMax: 10000,auth: {token: "123"},query: {"userId": "1"}});socket.on("connect", () => {console.log("通道已建立")});//监听服务端发送的 topic1 消息socket.on("exchange", (msg) => {console.log("收到新的数据了");var data = JSON.parse(msg)console.log(data);var type = data.typeif (type === 'invite') {to = data.from} else if (type === 'recive') {to = data.from} else if (type === 'canditate') {if (data.data != undefined) {pc1.addIceCandidate(new RTCIceCandidate(data.data))}} else if (type === 'offer') {//设置远程应答pc1.setRemoteDescription(new RTCSessionDescription(data.data))pc1.createAnswer().then(function (answer) {send({type: 'answer',data: answer})return pc1.setLocalDescription(answer)}).then(sdp => {})} else if (type === 'answer') {if (data.data != undefined) {pc1.setRemoteDescription(new RTCSessionDescription(data.data))}}});function gotStream(stream) {localVideo.srcObject = stream;localStream = stream;}async function init() {navigator.mediaDevices.getUserMedia({audio: true,video: false}).then(gotStream).catch(e => alert(`getUserMedia() error: ${e.name}`));}async function prepare() {init();var data = {type: 'invite',data: 'video',from: '1',toUserId: '2'};// send(data);prepareButton.disabled = true;}function call() {pc1 = new RTCPeerConnection(iceServer);pc1.onicecandidate = e => onIceCandidate(pc1, e);// 有远程视频流时,显示远程视频流 pc1.addEventListener('track', event => {// remoteVideo.srcObject = event.streams[0]// remoteVideo.onloadedmetadata = function (e) {// remoteVideo.play()// }})pc1.addEventListener('addstream', e => {console.log("AAAAAAAAAAA")console.log(e);remoteVideo.srcObject = e.stream})//初始化本人的视频流navigator.mediaDevices.getUserMedia({audio: false,video: true}).then(stream => {// 将视频流写入到video标签localVideo.srcObject = stream;localStream = stream;stream.getTracks().forEach(track => {pc1.addTrack(track, stream)});//向对方发送应答pc1.createOffer().then(sdp => {pc1.setLocalDescription(sdp)send({type: 'offer',data: sdp})})})localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));callBtn.disabled = true;}function onIceCandidate(pc, event) {if (event.candidate) {send({type: 'canditate',data: event.candidate})}}function send(data) {data.toUserId = data.toUserId || toconsole.log(data)socket.emit("exchange", JSON.stringify(data))}function callVideo() {call();}function cancelVideo() {if (pc1 != null && pc1 != undefined) {pc1.close();}}</script><style>#video {background-color: black;height: 30vh;}</style>
</body></html>
3、前端界面2代码如下(当界面1发起视频呼叫后,该界面被动应答):
<!DOCTYPE html>
<html lang="zh"><head><title>Socket.IO chat</title><meta charset="utf-8"><meta name="description" content="WebRTC code samples"><meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1"><meta itemprop="description" content="Client-side WebRTC code samples"><meta itemprop="name" content="WebRTC code samples"><meta name="mobile-web-app-capable" content="yes"><meta id="theme-color" name="theme-color" content="#ffffff"><!-- 引入2.0版本的socket.io文件 --><script src="socket.io.js"></script><script src="webrtc.js"></script><script src="jquery-3.6.4.js"></script><link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css"><link rel="stylesheet" href="css/main.css" /><link rel="stylesheet" href="css/c1/main.css" />
</head><body><div id="container"><video id="localVideo" title="a" playsinline autoplay muted></video><video id="remoteVideo" title="b" playsinline autoplay></video></div><script>const localVideo = document.getElementById('localVideo');const remoteVideo = document.getElementById('remoteVideo');let localStream;let pc1;let pc2;let to = '1';var iceServer = {{// urls: 'stun:stun.l.google.com:19302'urls: 'stun:stun.services.mozilla.com'}]};//建立socket连接const socket = io("https://192.168.3.21", {reconnectionDelayMax: 10000,auth: {token: "123"},query: {"userId": "2"}});socket.on("connect", () => {console.log("通道已建立")});//监听服务端发送的 topic1 消息socket.on("exchange", (msg) => {console.log("收到新的数据了");var data = JSON.parse(msg)var type = data.typeconsole.log({"name": "接收方",data});if (type === 'invite') {to = data.from//打开自己的摄像头init();} else if (type === 'recive') {to = data.from} else if (type === 'canditate') {if (pc1 == null) {init();}if (data.data != undefined) {pc1.addIceCandidate(new RTCIceCandidate(data.data))}} else if (type === 'offer') {if (pc1 == null) {init();}pc1.setRemoteDescription(new RTCSessionDescription(data.data))pc1.createAnswer().then(function (answer) {send({type: 'answer',data: answer})return pc1.setLocalDescription(answer)}).then(sdp => {})} else if (type === 'answer') {if (data.data != undefined) {pc1.setRemoteDescription(new RTCSessionDescription(data.data))}}});function gotStream(stream) {localVideo.srcObject = stream;localStream = stream;}function init() {navigator.mediaDevices.getUserMedia({audio: true,video: false}).then(gotStream).catch(e => alert(`getUserMedia() error: ${e.name}`));call();}function onIceCandidate(pc, event) {if (event.candidate) {send({type: 'canditate',data: event.candidate})}}function call() {pc1 = new RTCPeerConnection(iceServer);//获取本地网络信息,并发送给通信方 pc1.onicecandidate = e => onIceCandidate(pc1, e);// 有远程视频流时,显示远程视频流 // pc1.addEventListener('track', event => {// console.log("BBBBBBBBBBBBBBBBBBBBBBBBB")// console.log(event);// console.log(localVideo.srcObject)// console.log("BBBBBBBBBBBBBBBBBBBBBBBBB+")// remoteVideo.srcObject = event.streams[0]// remoteVideo.onloadedmetadata = function (e) {// console.log("加载完毕")// remoteVideo.play()// }// })pc1.addEventListener('addstream', e => {// 将对方的视频流写入到video标签中remoteVideo.srcObject = e.stream})//本地视频流量,打开视频流navigator.mediaDevices.getUserMedia({audio: false,video: true}).then(stream => {// 将视频流写入到video标签中 localVideo.srcObject = stream;localStream = stream;stream.getTracks().forEach(track => {pc1.addTrack(track, stream)})pc1.createOffer().then(sdp => {pc1.setLocalDescription(sdp)send({type: 'offer',data: sdp})})})}function send(data) {data.toUserId = data.toUserId || toconsole.log({"name": "发送",data})socket.emit("exchange", JSON.stringify(data))}</script><style>#video {background-color: black;height: 30vh;}</style>
</body></html>
WebRTC学习(六)端对端传输_51CTO博客_webrtc学习
webRTC结合webSocket实时通信 - 掘金
4、关于 coturn 信令服务的安装,参考以下文档:coturn安装以及报错“coturn/src/apps/relay/netengine.c:316:对‘SSL_CTX_up_ref’未定义的引用“_coturn 启动报错_Mango酱的博客-CSDN博客
5、coturn 安装后的测试:Trickle ICE (webrtc.github.io)
6、代码中进行如下设置:
//该参数为了var iceServer = {// iceServers: [{// urls: 'stun:stun.l.google.com:19302'// }]iceServers: [{urls: 'stun:stun.services.mozilla.com'},{urls: 'turn:ip:3478',credential: '密码',username: '账号'}]};
相关文章:
webRtc 示例
1、使用socket.io进行会话 2、为了方便,参数写死在前端了,前端界面1代码如下(由界面1发起视频): <!DOCTYPE html> <html><head><title>Socket.IO chat</title><meta charset"…...

【RabbitMQ】服务启动成功,无法访问localhost:15672(RabbitMQ Management)
问题描述 RabbitMQ 服务已经启动成功,已经安装rabbitmq_management插件,无法访问RabbitMQ Management(http://localhost:15672/)。 原因分析 15672端口被Microsoft Edge占用。 解决方案 打开cmd终端,输入指令&#…...

【操作记录】pytorch_geometric安装方法
pytorch_geometric安装方法 github地址 主要不要直接pip install安装,会由于依赖无法安装而失败 点击here手动安装依赖 选择对应的pytorch版本,我的是Win10 Python3.8.3Pytorch1.8.1CUDA10.2 手动下载四个依赖包本地安装: 主要不要直接&am…...
EventSystem 事件系统
EventSystem 事件系统 事件系统在开发中必不可少事件系统使用观察者模式可以极大程度降低程序的耦合,之前的文章也讲过事件系统但是不够高效简洁,如何轻便高效优雅的实现一个事件呢?依然基于之前的AssemblyManager 程序集管理器和SingletonS…...

2.2 Vector<T> 动态数组(模板语法)
C数据结构与算法 目录 本文前驱课程 1 C自学精简教程 目录(必读) 2 动态数组 Vector(难度1) 其中,2 是 1 中的一个作业。2 中详细讲解了动态数组实现的基本原理。 本文目标 1 学会写基本的C类模板语法; 2 为以后熟练使用 S…...

dockerfile 例子(二)
Dockerfile由一行一行的命令语句组成,#开头的为注释行。Dockerfile文件内容分为四个部分:基础镜像信息、维护者信息、镜像操作指令以及容器启动执行指令。 接下来给大家列出Dockerfile中主要命令的说明。 FROM,指定所创建镜像的基础镜像。 …...

openssh---Windows下git安装配置gitlab
安装openssh 1. 专业版Win10/11默认自带,可以查看是否开启 1. Get-WindowsCapability -Online | Where-Object Name -like OpenSSH* 2. Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0 3. Add-WindowsCapability -Online -Name OpenSSH.Serve…...

vscode宏键绑定
开发语言php 实现输入[ 得到 [];的效果 [win]ctrlp,[mac]superp 输入>keyboard 选择 在json文件里增加(目前有缺陷,sublime的设置是比较完美的.或者phpstorm默认不需要配置): {"key": "[","command": "editor.action.insertSnippet&…...

外贸企业如何借助CRM提升企业发展?
外贸企业竞争激烈,提高自身竞争力,扩大海外业务市场,是每个外贸企业的目标。为了实现这一目标,不少外贸企业借助CRM系统,优化业务流程,管理维护客户,从而实现可持续发展。那么,外贸企…...

初步了解ES
一、ES基础查询 1、es基础查询 1.1 准备数据 # 准备数据 PUT test_index/_doc/1 {"name":"顾老二","age":30,"from": "gu","desc": "皮肤黑、武器长、性格直","tags": ["黑", &…...
Linux基础(三)
一.系统基本优化 关闭selinux:getenforce 查看selinux状态setenforce 0 临时关闭vim /etc/sysconfig/selinux 永久关闭SELINUXdisabled 关闭防火墙:systemctl stop firewalld 临时关闭防火墙systemctl disable firewalld 永久关闭防火墙sys…...
python函数调用的四种方式
第一种:参数按顺序从第一个参数往后排#标准调用 def normal_invoke(x, y):print("--normal_invoke:--" )print("x is %d" %x )print("y is %d" %y) # 标准调用 normal_invoke(1, 2) 运行结果: --normal_invoke:-- x is 1 …...

如何将两个pdf合并成一个?pdf合并技巧分享
在日常工作过程当中,我们经常需要处理一些文件,而文件的处理往往是琐碎的,想要提高工作效率,需要选择一些合适的方法,并掌握一定的技巧,那么,如何将两个pdf合并成一个?pdf合并技巧有哪些呢?接…...
qt : day 3
1.完成登录框的按钮操作,并在登录成功后进行界面跳转 ------------------------------------------------------------------ .pro ------------------------------------------------------------------ QT core gui texttospeech greaterThan(QT_MAJOR_V…...

flutter高德地图大头针
1、效果图 2、pub get #地图定位 amap_flutter_map: ^3.0.0 amap_flutter_location: ^3.0.0 3、上代码 import dart:async; import dart:io;import package:amap_flutter_location/amap_flutter_location.dart; import package:amap_flutter_location/amap_location_option…...

【线性代数】矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇)
矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇) 说在前面一. 函数与标量、向量、矩阵二. 矩阵求导的本质三. 矩阵求导结果的布局四. 分子布局、分母布局的本质五. 向量变元的实值标量函数 说在前面 我将严谨地说明矩阵求导的本质与分子布…...

快速了解状态管理库Pinia及其使用方法
目录 1.pinia是什么 2.为什么要使用pinia 3.pinia的优点 4.pinia在项目中使用 ①创建一个使用pinia的Vue3项目 ②在页面使用store 1.pinia是什么 Pinia 起源于一次探索 Vuex 下一个迭代的实验,如果你学过Vue2,那么你一定使用过Vuex。Vuex在Vue2中主…...

scratch绘制同心圆 2023年5月中国电子学会图形化编程 少儿编程 scratch编程等级考试四级真题和答案解析
目录 scratch绘制同心圆 一、题目要求 1、准备工作 2、功能实现 二、案例分析 <...

【LeetCode】3. 无重复字符的最长子串
3. 无重复字符的最长子串(中等) 方法:滑动窗口 哈希表 思路 这道题主要用到思路是:滑动窗口 什么是滑动窗口? 其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 ab…...

苹果macOS 14开发者预览版Beta 7发布 新增超过100款视频壁纸和屏保
8 月 31 日,苹果向 Mac 电脑用户推送了 macOS 14 开发者预览版 Beta 7 更新(内部版本号:23A5337a),本次更新距离上次发布隔了 8 天。 苹果发布 Beta 7 更新的同时,还发布了第 6 个公测版,正式版…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...