Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案

原文首发链接:Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案
大家好,我是码农先森。

引言#

这次实现音视频实时通信的方案是基于 WebRTC 技术的,它是一种点对点的通信技术,通过浏览器之间建立对等连接,实现音频和视频流数据的传输。

在 WebRTC 技术中通常使用 WebSocket 服务来协调浏览器之间的通信,建立 WebRTC 通信的信道,传输通信所需的元数据信息,如:SDP、ICE 候选项等。

WebRTC 技术在实时通信领域中得到了广泛应用,包括在线会议、视频聊天、远程协作等,例如:腾讯在线会议就是基于此技术实现的。

技术实现#

index.html 作为首页,这里提供了发起方、接收方的操作入口。

Copy
<!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="/p2p?type=answer" target="_blank">接收方页面</a></li> <li>打开<a href="/p2p?type=offer" target="_blank">发起方页面</a></li> <li>确认双方都已建立 WebSocket 连接;</li> <li>发起方点击 开始 按钮。</li> </ul> </div> </body> </html>

p2p.html 作为视频展示页面,且实现了调取摄像头及音频权限的功能,再将连接数据推送到 WebSocket 服务端,最后渲染远程端的音视频数据到本地。

Copy
<!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: 330px; height: 550px; } #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: 140px; height: 200px; 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%); } </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()">开始</button> </div> </div> <script> 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 = () => { console.log('播放本地视频'); localVideo.play(); } remoteVideo.onloadeddata = () => { console.log('播放对方视频'); remoteVideo.play(); } document.title = target === 'offer' ? '发起方' : '接收方'; console.log('信令通道(WebSocket)创建中......'); const socket = new WebSocket('ws://127.0.0.1:9502'); socket.onopen = () => { console.log('信令通道创建成功!'); target === 'offer' && (button.style.display = 'block'); } socket.onerror = () => console.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 && console.error('浏览器不支持WebRTC!'); const peer = new PeerConnection(); peer.ontrack = e => { if (e && e.streams) { console.log('收到对方音频/视频流数据...'); remoteVideo.srcObject = e.streams[0]; } }; peer.onicecandidate = e => { if (e.candidate) { console.log('搜集并发送候选人'); socket.send(JSON.stringify({ type: `${target}_ice`, iceCandidate: e.candidate })); } else { console.log('候选人收集完成!'); } }; async function startLive (offerSdp) { target === 'offer' && (button.style.display = 'none'); let stream; try { console.log('尝试调取本地摄像头/麦克风'); stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); console.log('摄像头/麦克风获取成功!'); localVideo.srcObject = stream; } catch { console.error('摄像头/麦克风获取失败!'); return; } console.log(`------ WebRTC ${target === 'offer' ? '发起方' : '接收方'}流程开始 ------`); console.log('将媒体轨道添加到轨道集'); stream.getTracks().forEach(track => { peer.addTrack(track, stream); }); if (!offerSdp) { console.log('创建本地SDP'); const offer = await peer.createOffer(); await peer.setLocalDescription(offer); console.log(`传输发起方本地SDP`); socket.send(JSON.stringify(offer)); } else { console.log('接收到发送方SDP'); await peer.setRemoteDescription(offerSdp); console.log('创建接收方(应答)SDP'); const answer = await peer.createAnswer(); console.log(`传输接收方(应答)SDP`); socket.send(JSON.stringify(answer)); await peer.setLocalDescription(answer); } } </script> </body> </html>

在 http_server.php 文件中实现了一个 Web 服务,并根据不同的路由返回对应的 HTML 页面服务,主要是用于提供视频页面的展示。

Copy
<?php // 创建一个 HTTP 服务 $http = new Swoole\Http\Server("0.0.0.0", 9501); // 监听客户端请求 $http->on('request', function ($request, $response) { $path = $request->server['request_uri']; switch ($path) { case '/': $html = file_get_contents("index.html"); $response->header("Content-Type", "text/html"); $response->end($html); break; case '/p2p': $html = file_get_contents("p2p.html"); $response->header("Content-Type", "text/html"); $response->end($html); break; default: $response->status(404); $response->end("Page Not Found"); break; } }); // 启动 HTTP 服务 $http->start();

在 websocket_server.php 文件中实现了一个 WebSocket 服务,并设置了 onOpen、onMessage 和 onClose 回调函数。在 onMessage 回调函数中,遍历所有连接,将消息发送给除当前连接外的其他连接。

Copy
<?php // 创建 WebSocket 服务 $server = new Swoole\WebSocket\Server("0.0.0.0", 9502); // 监听 WebSocket 连接事件 $server->on('open', function (Swoole\WebSocket\Server $server, $request) { echo "新的客户端连接: {$request->fd}\n"; }); // 监听 WebSocket 消息事件 $server->on('message', function (Swoole\WebSocket\Server $server, $frame) { echo "收到消息来自 {$frame->fd}: {$frame->data}, 广播给其他的客户端\n"; // 广播给其他的客户端 foreach ($server->connections as $fd) { if ($fd != $frame->fd) { $server->push($fd, $frame->data); } } }); // 监听 WebSocket 关闭事件 $server->on('close', function ($ser, $fd) { echo "客户端 {$fd} 关闭连接\n"; }); // 启 WebSocket 服务 $server->start();

总结#

音视频通信技术方案是基于 WebRTC 实现的,Swoole 在其中的作用是提供了页面的 Web 服务及协调浏览器之间通信的 WebSocket 服务。
WebRTC 是一项重要的技术,它使得实时音视频通信变得简单而高效。通过基于浏览器的 API,WebRTC 可以实现点对点的音视频通信。

posted @   Yxh_blogs  阅读(91)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
CONTENTS