socket实现视频通话-WebRTC

最近喜欢研究视频流,所以思考了双向通信socket,接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~

客户端获取视频流

首先思考如何获取视频流呢?

其实跟录音的功能差不多,都是查询电脑上是否有媒体设备,如果有录音和录像的设备,首先就需要授权,然后将视频流通过socket传输给服务端。

获取媒体设备

const stream = await navigator.mediaDevices.getUserMedia({
  audio: true,
  video: true
})

因为是打视频的功能,那A客户端本身也希望看到A的摄像头,所以我们直接将其赋值给一个video标签,就能看到图像了.

<p>这是A页面</p>

<div class="local-stream-page">
  <video autoplay controls muted id="elA"></video>
  <button onclick="onStart()">打视频给B页面</button>
</div>

<script>

  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: true,
      video: true
    })
    if (videoElA) {
      videoElA.srcObject = stream // 在 video 标签上播放媒体流
    }
    peerInit(stream) // 初始化连接
  } catch (error) {
    console.log('error:', error)
  }

</script>

然后就是重要部分了,我们需要用到WebRTC的API

RTCPeerConnection

RTCPeerConnection是WebRTC API中的一个对象,用于建立和管理两个或多个用户之间的实时通信。它允许通过互联网进行音频和视频通话,以及共享数据流。

RTCPeerConnection对象提供了一系列的方法和事件,用于配置、管理和控制媒体流的传输。它支持使用不同的技术,如ICE(Interactive Connectivity Establishment)和STUN(Session Traversal Utilities for NAT)来解决网络地址转换(NAT)问题,以便在防火墙后面的不同设备之间建立连接。

使用RTCPeerConnection对象,您可以创建媒体流并将其发送到其他设备,也可以接收来自其他设备的媒体流。它还支持使用SDP(Session Description Protocol)描述媒体会话的配置,以及通过ICE和STUN协议协商和转发媒体数据包的路由。

const peerInit = stream => {
  // 1. 创建连接实例
  peerA = new RTCPeerConnection()
  // 2. 添加视频流轨道
  stream.getTracks().forEach(track => {
    peerA.addTrack(track, stream)
  })


  // peerA 端
  peerA.onicecandidate = event => {
    if (event.candidate) {
      socketA.send(JSON.stringify({ type: 'candid', data: event.candidate })) // socketA发送数据
    }
  }
  // 检测连接状态
  peerA.onconnectionstatechange = event => {
    if (peerA.connectionState === 'connected') {
      console.log('对等连接成功!')
    }
  }

  // 互换sdp认证
  transSDP()
}

到这里我们发送数据部分就是这样子啦,但是还不行,因为两者通视频,还需要SDP认证,什么是SDP认证呢?

SDP(Session Description Protocol)认证是指通过在SDP协议中添加特定的信息来验证身份或其他属性的方法。SDP协议是一种用于描述多媒体会话的信息协议,它包含了音频、视频等媒体的编码格式、分辨率、网络地址等信息,用于在通话双方之间建立和维护媒体连接。

在SDP认证中,通过在SDP协议中添加特定的信息,如用户名、会议ID等,双方可以互相验证身份。此外,还可以通过在SDP协议中包含数字签名或加密信息等技术来增强认证的安全性。

SDP认证通常用于多媒体通信、视频会议等应用场景中,以确保通信的安全性和可信度。在SDP认证中,需要使用相应的协议或算法来验证SDP信息的来源和完整性,以确认身份或其他属性的合法性。

互换SDP认证

// peerA 端
const transSDP = async () => {
 
  let offer = await peerA.createOffer()
  // 向 peerB 传输 offer
  socketA.send(JSON.stringify({ type: 'offer', data: offer }))
  // 接收 peerB 传来的 answer
  socketA.onmessage = async evt => {
    
    let reader = new FileReader()
    reader.readAsText(evt.data, 'utf-8')
    reader.onload = async function() {
      let { type, data } = JSON.parse(reader.result)
      console.log(JSON.parse(reader.result), 111)
      if (type == 'answer') {
        await peerA.setLocalDescription(offer)
        await peerA.setRemoteDescription(data)
      }
    }
    
  }
}

这就是A客户端的全部代码啦~

放心,全部代码文章末尾会给到.

node服务端socket传输

接下来我们来看看服务端是如何处理的.对了,这里必须说一下,两个socket之间的通信,必须要靠服务端管理,所以这就是为什么一定要学node的原因😂

const WebSocket = require('ws');

// 创建一个 WebSocket 服务器,监听 8080 端口
const wss = new WebSocket.Server({ port: 8000 });

// 当有客户端连接时,创建一个 WebSocket 并将其添加到客户端列表中
wss.on('connection', function connection(ws) {
  console.log('Client connected');

  // 当客户端发送消息时,将消息发送给所有客户端
  ws.on('message', function incoming(message) {
    console.log('Received message:', message.toString('utf8')); // 接受的对象,客户端发送的是字符串,Buffer

    // 将消息发送给所有客户端
    wss.clients.forEach(function each(client) {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message); // 客户端接受的是blob格式数据
      }
    });
  });

  // 当客户端断开连接时,将其从客户端列表中删除
  ws.on('close', function close() {
    console.log('Client disconnected');
  });
});

服务端用到了ws依赖, 如何区分两个不同的socket客户端, 特别是在同一个服务器下,同一个端口,不同的页面下,我发现必须要给两个socket一个唯一的标识才能做到,所以这期就先出功能,后面再继续补一下ws的源码学习.

不过这里要区分清楚,这是将当前的client客户端发送给处理自己以外的,其他所以socket客户端,发送消息这里,就是一对多的关系哦.

客户端接受视频流

服务端处理完了,就进行下一个客户端如何接受视频流

刚刚的sdp认证,肯定不止止A页面的事情,都说了是认证,那肯定通信双方需要知晓.

这里有一个顺序问题

1.首先是A页面创建offer—createOffer

2.然后是B页面设置远程描述—setRemoteDescription

3.B页面生成发送到A页面的answer—createAnswer

4.B页面设置本地描述—setLocalDescription

5.A页面设置本地描述—setLocalDescription(传参是A页面的offer)

6.A页面设备远程描述–setRemoteDescription(传参是B页面的answer)

只要这上面6步都正常执行,B页面才能接收到A页面的视频流和音频流

const transSDP = async () => {
  // 1. 创建 offer
  let offer = await peerA.createOffer()
  await peerB.setRemoteDescription(offer)
  // 2. 创建 answer
  let answer = await peerB.createAnswer()
  await peerB.setLocalDescription(answer)
  // 3. 发送端设置 SDP
  await peerA.setLocalDescription(offer)
  await peerA.setRemoteDescription(answer)
}

加上socket之后就是这样

不过既然是socket了,所以数据上要做转换处理,接收到的是blob数据

// B接收A的消息
// peerB 端,接收 peerA 传来的 offer
socketB.onmessage = evt => {
  // console.log(evt.data)
  handleBlobToText(evt.data)
}

const handleBlobToText = (blob) => {
  let reader = new FileReader()
  reader.readAsText(blob, 'utf-8') // 接收到的是blob数据,先转成文本
  reader.onload = async function() {
    console.log(reader.result)
    let { type, data } = JSON.parse(reader.result) // 文本转对象
    console.log(JSON.parse(reader.result))
    if (type == 'offer') {
      await peerB.setRemoteDescription(data)
      console.log('2.然后是B页面设置远程描述', new Date().getTime())
      let answer = await peerB.createAnswer()
      console.log('3.B页面生成发送到A页面的answer', new Date().getTime())
      await peerB.setLocalDescription(answer)
      console.log('4.B页面设置本地描述', new Date().getTime())
      // 向 peerA 传输 answer
      socketB.send(JSON.stringify({ type: 'answer', data: answer }))
    }
    if (type == 'candid') {
      peerB.addIceCandidate(data)
    }
  }
}

socketB.onerror = function() {
  console.log('WebSocket error. Ready state:', socketB.readyState);
};

根据时间戳,就能发现这六步的顺序.

将接收到的视频流渲染到B页面的video标签中,这就能接受的A页面的视频流了.

const socketB = new WebSocket('ws://localhost:8000');

const peerB = new RTCPeerConnection()
const videoElB = document.getElementById('elB')

// 监听数据传来
peerB.ontrack = async event => {
  const [remoteStream] = event.streams
  videoElB.srcObject = remoteStream
}

效果

这就是两个页面视频通讯的结果如下:

在这里插入图片描述

全部源码已经上传在GitHub上啦~

https://github.com/0522skylar/webRTC-video

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

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

相关文章

杰发科技AC7840——Eclipse环境DMA注意事项

0.序 用 户 使 用 DMA 时 &#xff0c; 所 有 DMA 搬 运 的 SRAM 数 据 都 必 须 存 放 在 SRAM_U 区 (0x20000000~0x2000EFFF) 。 1. 修改办法 第一步&#xff1a; RAM定义 /* Specify the memory areas */ MEMORY {FLASH (rx) : ORIGIN 0x00000000, LENGT…

多功能号卡推广分销管理系统 流量卡推广分销网站源码

一套完善&#xff0c;多功能&#xff0c;的号卡分销系统&#xff0c;多接口&#xff0c;包括运营商接口&#xff0c;无限三级代理&#xff0c;最简单易用的PHP~ 目前市面上最优雅的号卡系统&#xff01;没有之一 软件架构说明 环境要求php7.3以上&#xff08;建议低于8.0&…

面试题理解深层次的数组名

目录 引言 一&#xff1a;一维数组 举例如下 1.铺垫知识 数组名是数组首元素的地址&#xff0c;但是有两个特殊情况 &#xff08;1&#xff09;sizeof(数组名) &#xff08;2&#xff09;&数组名 2.分析讲解上述代码结果 2.字符数组 举例一如下 1.知识铺垫 …

Python内置库os和sys的常用方法汇总

更多Python学习内容&#xff1a;ipengtao.com Python是一门强大的编程语言&#xff0c;具有丰富的标准库&#xff0c;其中包括os和sys两个常用模块。os模块用于与操作系统交互&#xff0c;提供了许多文件和目录操作的方法&#xff0c;而sys模块用于与Python解释器进行交互&…

OceanBase入选Gartner®云数据库管理系统魔力象限“荣誉提及”

近日&#xff0c;全球IT市场研究和咨询公司Gartner发布最新报告《Magic Quadrant™ for Cloud Database Management Systems》&#xff08;全球云数据库管理系统魔力象限&#xff09;。全自研分布式数据库 OceanBase 入选“荣誉提及”&#xff0c;2022 年推出的云数据库 OB Clo…

uniapp---安卓真机调试提示检测不到手机【解决办法】

最近在做APP&#xff0c;由于华为手机更新过系统&#xff0c;再次用来调试APP发现就不行了。下面给出具体的解决方法&#xff1a; 第一步&#xff1a;打开【允许开发人员选项】 找到【设置】点击【关于手机】找到【版本号】点击7次或多次&#xff0c;允许开发人员选项。 第二…

性能优化:Spark SQL中的谓词下推和列式存储

Apache Spark是一个强大的分布式计算框架&#xff0c;Spark SQL是其一个核心模块&#xff0c;用于处理结构化数据。性能优化是大数据处理中的一个关键问题&#xff0c;本文将深入探讨Spark SQL中的两个性能优化技术&#xff1a;谓词下推&#xff08;Predicate Pushdown&#xf…

Ps:应用图像

Ps菜单&#xff1a;图像/应用图像 Image/Apply Image 应用图像 Apply Image命令可以将图像的内容&#xff08;作为“源”&#xff09;叠加到另一图像&#xff08;作为“目标”&#xff09;上&#xff0c;同时提供了控制混合的详细选项。 “应用图像”命令在图像合成、图层蒙版和…

彻底理解前端安全面试题(2)—— CSRF 攻击,跨站请求伪造攻击详解,建议收藏(含源码)

前言 前端关于网络安全看似高深莫测&#xff0c;其实来来回回就那么点东西&#xff0c;我总结一下就是 3 1 4&#xff0c;3个用字母描述的【分别是 XSS、CSRF、CORS】 一个中间人攻击。当然 CORS 同源策略是为了防止攻击的安全策略&#xff0c;其他的都是网络攻击。除了这…

音视频通信

文章目录 一、音视频通信流程二、流媒体协议1、RTSP2、RTMP3、HLS4、WebRTC 一、音视频通信流程 音视频通信完整流程有如下几个环节&#xff1a;采集、编码、前后处理、传输、解码、缓冲、渲染等。 每一个细分环节&#xff0c;还有更细分的技术模块。比如&#xff0c;前后处…

网络端口(包括TCP端口和UDP端口)的作用、定义、分类,以及在视频监控和流媒体通信中的定义

目 录 一、什么地方会用到网络端口&#xff1f; 二、端口的定义和作用 &#xff08;一&#xff09;TCP协议和UDP协议 &#xff08;二&#xff09;端口的定义 &#xff08;三&#xff09;在TCP/IP体系中&#xff0c;端口(TCP和UDP)的作用 &#xff08;…

canvas绘制圆角矩形示例

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

【期末考试】数据结构大题

~~08|2.03|2|8 ^^统计出没带头结点的单链表HL中结点的值等于给定值x的结点数。 int CountX(LNode *HL, ElemType x) {int i 0;LNode *p HL;while (p ! NULL){if (P->data x)i;p p->next;}return i; }简答&#xff1a;定义一个i变量来记录节点值等于x的节点个数&…

Debezium发布历史43

原文地址&#xff1a; https://debezium.io/blog/2018/12/05/automating-cache-invalidation-with-change-data-capture/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. 通过更改数据捕获自动使缓存失效 2018 年…

以 RoCE+软件定义存储同时实现信创转型与架构升级

目前&#xff0c;不少企业数据中心使用 FC 交换机和集中式 SAN 存储&#xff08;以下简称“FC-SAN 架构”&#xff09;&#xff0c;支持核心业务系统、数据库、AI/ML 等高性能业务场景。而在开展 IT 基础架构信创转型时&#xff0c;很多用户受限于国外交换机&#xff1a;FC 交换…

20 太空漫游

效果演示 实现了一个太空漫游的动画效果&#xff0c;其中包括火箭、星星和月亮。当鼠标悬停在卡片上时&#xff0c;太阳和星星会变成黄色&#xff0c;火箭会变成飞机&#xff0c;月亮会变成小型的月亮。整个效果非常炫酷&#xff0c;可以让人想起科幻电影中的太空漫游。 Code &…

SpringBoot—支付—支付宝

一、流程 二、沙箱操作 1.用支付宝账号登录【开放控制平台】创建应用获取 appid 2.选择沙箱模拟环境 3.沙箱应用-》获取appid(一个appid绑定一个收款支付宝账户) 4.利用开发助手工具生成RSA2密钥 公钥&#xff1a;传给支付宝平台 私钥&#xff1a;配置代码中&#xff0c;…

json解析本地数据,使用JSONObject和JsonUtility两种方法。

json解析丨网址、数据、其他信息 文章目录 json解析丨网址、数据、其他信息介绍一、文中使用了两种方法作为配置二、第一种准备2.代码块 二、第二种总结 介绍 本文可直接解析本地json信息的功能示例&#xff0c;使用JSONObject和JsonUtility两种方法。 一、文中使用了两种方法…

[GKCTF 2020]ez三剑客-eztypecho

[GKCTF 2020]ez三剑客-eztypecho 考点&#xff1a;Typecho反序列化漏洞 打开题目&#xff0c;发现是typecho的CMS 尝试跟着创建数据库发现不行&#xff0c;那么就搜搜此版本的相关信息发现存在反序列化漏洞 参考文章 跟着该文章分析来&#xff0c;首先找到install.php&#xf…

Arduino开发实例-AD8232心率监测传感器驱动

AD8232心率监测传感器驱动 文章目录 AD8232心率监测传感器驱动1、AD8232介绍2、硬件准备及接线3、驱动实现1、AD8232介绍 AD8232 传感器可为您提供心电图或 ECG 信号监测。 分析这些信号可以提供有关心脏功能的有用信息,例如心跳率、心律和其他有关心脏状况的信息。 该模块可…