阅读背景:

最简单的webrtc p2p demo

来源:互联网 
最简单的webrtc p2p demo 之前没看过js的服务,nodejs 看起来做demo好方便 github nodej启动服务 需要express 和 express-ws 启动websocket 协议的服务,监听8080端口 router有 / 和 /p2p 首先访问 / ,触发遍历每个client,向每个client发送 data / 的 data 是./client/index.html ./client/index.html 内容 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>p2p webrtc</title> <style> .container { width: 250px; margin: 100px auto; padding: 10px 30px; border-radius: 4px; border: 1px solid #ebeef5; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); color: #303133; } </style> </head> <body> <div class="container"> <p>流程:</p> <ul> <li>打开<a href="/go.html?url=/p2p?type=answer" target="_blank">接收方页面</a>;</li> <li>打开<a href="/go.html?url=/p2p?type=offer" target="_blank">发起方页面</a>;</li> <li>确认双方都已建立ws连接;</li> <li>发起方点击 start 按钮。</li> </ul> </div> </body> </html> 主要是有俩链接 接收是answer 发送是 offer 点击链接后 会做为一个客户端,向nodejs发送请求 nodejs会发 ./client/p2p.html 给客户端 <li>打开<a href="/go.html?url=/p2p?type=answer" target="_blank">接收方页面</a>;</li> <li>打开<a href="/go.html?url=/p2p?type=offer" target="_blank">发起方页面</a>;</li> <li>确认双方都已建立ws连接;</li> <li>发起方点击 start 按钮。</li> code const app = require('express')(); const wsInstance = require('express-ws')(app); app.ws('/', ws => { ws.on('message', data => { // 未做业务处理,收到消息后直接广播 wsInstance.getWss().clients.forEach(server => { if (server !== ws) { server.send(data); } }); }); }); app.get('/', (req, res) => { res.sendFile('./client/index.html', { root: __dirname }); }); app.get('/p2p', (req, res) => { res.sendFile('./client/p2p.html', { root: __dirname }); }); app.listen(8080); 客户端收到 ./client/p2p.html code <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title></title> <style> * { padding: 0; margin: 0; box-sizing: border-box; } .container { width: 100%; display: flex; display: -webkit-flex; justify-content: space-around; padding-top: 20px; } .video-box { position: relative; width: 800px; height: 400px; } #remote-video { width: 100%; height: 100%; display: block; object-fit: cover; border: 1px solid #eee; background-color: #F2F6FC; } #local-video { position: absolute; right: 0; bottom: 0; width: 240px; height: 120px; object-fit: cover; border: 1px solid #eee; background-color: #EBEEF5; } .start-button { position: absolute; left: 50%; top: 50%; width: 100px; display: none; line-height: 40px; outline: none; color: #fff; background-color: #409eff; border: none; border-radius: 4px; cursor: pointer; transform: translate(-50%, -50%); } .logger { width: 40%; padding: 14px; line-height: 1.5; color: #4fbf40; border-radius: 6px; background-color: #272727; } .logger .error { color: #DD4A68; } </style> </head> <body> <div class="container"> <div class="video-box"> <video id="remote-video"></video> <video id="local-video" muted></video> <button class="start-button" onclick="startLive()">start</button> </div> <div class="logger"></div> </div> <script> const message = { el: document.querySelector('.logger'), log (msg) { this.el.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`; }, error (msg) { this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`; } }; const target = location.search.slice(6); const localVideo = document.querySelector('#local-video'); const remoteVideo = document.querySelector('#remote-video'); const button = document.querySelector('.start-button'); localVideo.onloadeddata = () => { message.log('播放本地视频'); localVideo.play(); } remoteVideo.onloadeddata = () => { message.log('播放对方视频'); remoteVideo.play(); } document.title = target === 'offer' ? '发起方' : '接收方'; message.log('信令通道(WebSocket)创建中......'); const socket = new WebSocket('ws://localhost:8080'); socket.onopen = () => { message.log('信令通道创建成功!'); target === 'offer' && (button.style.display = 'block'); } socket.onerror = () => message.error('信令通道创建失败!'); socket.onmessage = e => { const { type, sdp, iceCandidate } = JSON.parse(e.data) if (type === 'answer') { peer.setRemoteDescription(new RTCSessionDescription({ type, sdp })); } else if (type === 'answer_ice') { peer.addIceCandidate(iceCandidate); } else if (type === 'offer') { startLive(new RTCSessionDescription({ type, sdp })); } else if (type === 'offer_ice') { peer.addIceCandidate(iceCandidate); } }; const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; !PeerConnection && message.error('浏览器不支持WebRTC!'); const peer = new PeerConnection(); peer.ontrack = e => { if (e && e.streams) { message.log('收到对方音频/视频流数据...'); remoteVideo.srcObject = e.streams[0]; } }; peer.onicecandidate = e => { if (e.candidate) { message.log('搜集并发送候选人'); socket.send(JSON.stringify({ type: `${target}_ice`, iceCandidate: e.candidate })); } else { message.log('候选人收集完成!'); } }; async function startLive (offerSdp) { target === 'offer' && (button.style.display = 'none'); let stream; try { message.log('尝试调取本地摄像头/麦克风'); stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); message.log('摄像头/麦克风获取成功!'); localVideo.srcObject = stream; } catch { message.error('摄像头/麦克风获取失败!'); return; } message.log(`------ WebRTC ${target === 'offer' ? '发起方' : '接收方'}流程开始 ------`); message.log('将媒体轨道添加到轨道集'); stream.getTracks().forEach(track => { peer.addTrack(track, stream); }); if (!offerSdp) { message.log('创建本地SDP'); const offer = await peer.createOffer(); await peer.setLocalDescription(offer); message.log(`传输发起方本地SDP`); socket.send(JSON.stringify(offer)); } else { message.log('接收到发送方SDP'); await peer.setRemoteDescription(offerSdp); message.log('创建接收方(应答)SDP'); const answer = await peer.createAnswer(); message.log(`传输接收方(应答)SDP`); socket.send(JSON.stringify(answer)); await peer.setLocalDescription(answer); } } </script> </body> </html> p2p 作为websocket的客户端,向服务端8080端口建立websocket连接 收到socket的消息,要做解析,json格式? 消息类型 sdk文件 icecandidate 收到answer ,自己就设置对端的sdp 收到answer 活着 offer 的ice,自己就设置ice 收到offer,自己就开始使用对端的sdp做播放?? socket.onmessage = e => { const { type, sdp, iceCandidate } = JSON.parse(e.data) if (type === 'answer') { peer.setRemoteDescription(new RTCSessionDescription({ type, sdp })); } else if (type === 'answer_ice') { peer.addIceCandidate(iceCandidate); } else if (type === 'offer') { startLive(new RTCSessionDescription({ type, sdp })); } else if (type === 'offer_ice') { peer.addIceCandidate(iceCandidate); } }; 打印,带显示时间的 const message = { el: document.querySelector('.logger'), log (msg) { this.el.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`; }, error (msg) { this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`; } }; 这好像是获取页面上对应的元素对象 本地 远程 开始按钮 const target = location.search.slice(6); const localVideo = document.querySelector('#local-video'); const remoteVideo = document.querySelector('#remote-video'); const button = document.querySelector('.start-button'); 网页标题 document.title = target === 'offer' ? '发起方' : '接收方'; 收到本地的数据就play本地,收到对方的数据就play对端 localVideo.onloadeddata = () => { message.log('播放本地视频'); localVideo.play(); } remoteVideo.onloadeddata = () => { message.log('播放对方视频'); remoteVideo.play(); } 创建peerconnection peer 应该指的p2p里面的对端 peer里处理的是对端的音视频 比如 remoteVideo 拿到对端的stream 进行播放 const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; !PeerConnection && message.error('浏览器不支持WebRTC!'); const peer = new PeerConnection(); 在peer做处理 track 记录track里的每个流 ice ,会通过socket发送到对端??? peer.ontrack = e => { if (e && e.streams) { message.log('收到对方音频/视频流数据...'); remoteVideo.srcObject = e.streams[0]; } }; peer.onicecandidate = e => { if (e.candidate) { message.log('搜集并发送候选人'); socket.send(JSON.stringify({ type: `${target}_ice`, iceCandidate: e.candidate })); } else { message.log('候选人收集完成!'); } }; 使用 SDP 直播 : startLive 1 getUserMedia 获取用户的音视频设备,这样拿到stream 2 从流里面拿到track 3 非offer的sdp 就是本地自己的sdp TODO 回头再来看把,这里还要发送sdp到对端 然后才设置 async function startLive (offerSdp) { target === 'offer' && (button.style.display = 'none'); let stream; try { message.log('尝试调取本地摄像头/麦克风'); stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); message.log('摄像头/麦克风获取成功!'); localVideo.srcObject = stream; } catch { message.error('摄像头/麦克风获取失败!'); return; } message.log(`------ WebRTC ${target === 'offer' ? '发起方' : '接收方'}流程开始 ------`); message.log('将媒体轨道添加到轨道集'); stream.getTracks().forEach(track => { peer.addTrack(track, stream); }); if (!offerSdp) { message.log('创建本地SDP'); const offer = await peer.createOffer(); await peer.setLocalDescription(offer); message.log(`传输发起方本地SDP`); socket.send(JSON.stringify(offer)); } else { message.log('接收到发送方SDP'); await peer.setRemoteDescription(offerSdp); message.log('创建接收方(应答)SDP'); const answer = await peer.createAnswer(); message.log(`传输接收方(应答)SDP`); socket.send(JSON.stringify(answer)); await peer.setLocalDescription(answer); } } 综上 里面有信令的交互 TODO sdp 是通过信令通道来的?ice好像也是信令做的? 通过peerconnection拿到stream mac 安装 npm run dev 才能打开8080 服务 npm install 只是安装依赖库 ✘ zhangbin@pb6a80114  ~/tet/licodelllcode  git clone https://github.com/shushushv/webrtc-p2p.git Cloning into 'webrtc-p2p'... remote: Enumerating objects: 12, done. remote: Counting objects: 100% (12/12), done. remote: Compressing objects: 100% (9/9), done. remote: Total 12 (delta 0), reused 9 (delta 0), pack-reused 0 Unpacking objects: 100% (12/12), done. zhangbin@pb6a80114  ~/tet/licodelllcode  cd webrtc-p2p zhangbin@pb6a80114  ~/tet/licodelllcode/webrtc-p2p   master  npm install npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN [email protected] No repository field. added 53 packages from 45 contributors and audited 129 packages in 4.953s found 0 vulnerabilities ╭────────────────────────────────────────────────────────────────╮ │ │ │ New minor version of npm available! 6.9.0 → 6.13.1 │ │ Changelog: https://github.com/npm/cli/releases/tag/v6.13.1 │ │ Run npm install -g npm to update! │ │ │ ╰────────────────────────────────────────────────────────────────╯ zhangbin@pb6a80114  ~/tet/licodelllcode/webrtc-p2p   master  npm run dev > [email protected] dev /Users/zhangbin/tet/licodelllcode/webrtc-p2p > node index.js 最简单的webrtc p2p demo 之前没看过js的服务,nodejs 看起来做demo好方便



你的当前访问异常,请进行认证后继续阅读剩余内容。

分享到: