大连小程序哪个开发公司好,seo方式包括,wordpress运行目录,网站欢迎页设计最近喜欢研究视频流#xff0c;所以思考了双向通信socket#xff0c;接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~
客户端获取视频流
首先思考如何获取视频流呢#xff1f;
其实跟录音的功能差不多#xff0c;都是查询电脑上是否有媒体设备#xff0c;如果…最近喜欢研究视频流所以思考了双向通信socket接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~
客户端获取视频流
首先思考如何获取视频流呢
其实跟录音的功能差不多都是查询电脑上是否有媒体设备如果有录音和录像的设备首先就需要授权然后将视频流通过socket传输给服务端。
获取媒体设备
const stream await navigator.mediaDevices.getUserMedia({audio: true,video: true
})因为是打视频的功能那A客户端本身也希望看到A的摄像头所以我们直接将其赋值给一个video标签就能看到图像了.
p这是A页面/pdiv classlocal-stream-pagevideo autoplay controls muted idelA/videobutton onclickonStart()打视频给B页面/button
/divscripttry {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对象提供了一系列的方法和事件用于配置、管理和控制媒体流的传输。它支持使用不同的技术如ICEInteractive Connectivity Establishment和STUNSession Traversal Utilities for NAT来解决网络地址转换NAT问题以便在防火墙后面的不同设备之间建立连接。
使用RTCPeerConnection对象您可以创建媒体流并将其发送到其他设备也可以接收来自其他设备的媒体流。它还支持使用SDPSession 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认证呢
SDPSession Description Protocol认证是指通过在SDP协议中添加特定的信息来验证身份或其他属性的方法。SDP协议是一种用于描述多媒体会话的信息协议它包含了音频、视频等媒体的编码格式、分辨率、网络地址等信息用于在通话双方之间建立和维护媒体连接。
在SDP认证中通过在SDP协议中添加特定的信息如用户名、会议ID等双方可以互相验证身份。此外还可以通过在SDP协议中包含数字签名或加密信息等技术来增强认证的安全性。
SDP认证通常用于多媒体通信、视频会议等应用场景中以确保通信的安全性和可信度。在SDP认证中需要使用相应的协议或算法来验证SDP信息的来源和完整性以确认身份或其他属性的合法性。
互换SDP认证
// peerA 端
const transSDP async () {let offer await peerA.createOffer()// 向 peerB 传输 offersocketA.send(JSON.stringify({ type: offer, data: offer }))// 接收 peerB 传来的 answersocketA.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. 创建 offerlet offer await peerA.createOffer()await peerB.setRemoteDescription(offer)// 2. 创建 answerlet answer await peerB.createAnswer()await peerB.setLocalDescription(answer)// 3. 发送端设置 SDPawait 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 传输 answersocketB.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.streamsvideoElB.srcObject remoteStream
}效果
这就是两个页面视频通讯的结果如下 全部源码已经上传在GitHub上啦~
https://github.com/0522skylar/webRTC-video