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

2024-04-17 04:12

本文主要是介绍Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

引言

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

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

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

技术实现

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="/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 服务端,最后渲染远程端的音视频数据到本地。

<!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 页面服务,主要是用于提供视频页面的展示。

<?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 回调函数中,遍历所有连接,将消息发送给除当前连接外的其他连接。

<?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 可以实现点对点的音视频通信。

这篇关于Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/910765

相关文章

Python实现AVIF图片与其他图片格式间的批量转换

《Python实现AVIF图片与其他图片格式间的批量转换》这篇文章主要为大家详细介绍了如何使用Pillow库实现AVIF与其他格式的相互转换,即将AVIF转换为常见的格式,比如JPG或PNG,需要的小... 目录环境配置1.将单个 AVIF 图片转换为 JPG 和 PNG2.批量转换目录下所有 AVIF 图

Pydantic中model_validator的实现

《Pydantic中model_validator的实现》本文主要介绍了Pydantic中model_validator的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录引言基础知识创建 Pydantic 模型使用 model_validator 装饰器高级用法mo

AJAX请求上传下载进度监控实现方式

《AJAX请求上传下载进度监控实现方式》在日常Web开发中,AJAX(AsynchronousJavaScriptandXML)被广泛用于异步请求数据,而无需刷新整个页面,:本文主要介绍AJAX请... 目录1. 前言2. 基于XMLHttpRequest的进度监控2.1 基础版文件上传监控2.2 增强版多

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

Redis分片集群的实现

《Redis分片集群的实现》Redis分片集群是一种将Redis数据库分散到多个节点上的方式,以提供更高的性能和可伸缩性,本文主要介绍了Redis分片集群的实现,具有一定的参考价值,感兴趣的可以了解一... 目录1. Redis Cluster的核心概念哈希槽(Hash Slots)主从复制与故障转移2.

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

Docker镜像修改hosts及dockerfile修改hosts文件的实现方式

《Docker镜像修改hosts及dockerfile修改hosts文件的实现方式》:本文主要介绍Docker镜像修改hosts及dockerfile修改hosts文件的实现方式,具有很好的参考价... 目录docker镜像修改hosts及dockerfile修改hosts文件准备 dockerfile 文

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable