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

相关文章

C#高效实现在Word文档中自动化创建图表的可视化方案

《C#高效实现在Word文档中自动化创建图表的可视化方案》本文将深入探讨如何利用C#,结合一款功能强大的第三方库,实现在Word文档中自动化创建图表,为你的数据呈现和报告生成提供一套实用且高效的解决方... 目录Word文档图表自动化:为什么选择C#?从零开始:C#实现Word文档图表的基本步骤深度优化:C

nginx跨域访问配置的几种方法实现

《nginx跨域访问配置的几种方法实现》本文详细介绍了Nginx跨域配置方法,包括基本配置、只允许指定域名、携带Cookie的跨域、动态设置允许的Origin、支持不同路径的跨域控制、静态资源跨域以及... 目录一、基本跨域配置二、只允许指定域名跨域三、完整示例四、配置后重载 nginx五、注意事项六、支持

Qt实现对Word网页的读取功能

《Qt实现对Word网页的读取功能》文章介绍了几种在Qt中实现Word文档(.docx/.doc)读写功能的方法,包括基于QAxObject的COM接口调用、DOCX模板替换及跨平台解决方案,重点讨论... 目录1. 核心实现方式2. 基于QAxObject的COM接口调用(Windows专用)2.1 环境

MySQL查看表的历史SQL的几种实现方法

《MySQL查看表的历史SQL的几种实现方法》:本文主要介绍多种查看MySQL表历史SQL的方法,包括通用查询日志、慢查询日志、performance_schema、binlog、第三方工具等,并... 目录mysql 查看某张表的历史SQL1.查看MySQL通用查询日志(需提前开启)2.查看慢查询日志3.

Java实现字符串大小写转换的常用方法

《Java实现字符串大小写转换的常用方法》在Java中,字符串大小写转换是文本处理的核心操作之一,Java提供了多种灵活的方式来实现大小写转换,适用于不同场景和需求,本文将全面解析大小写转换的各种方法... 目录前言核心转换方法1.String类的基础方法2. 考虑区域设置的转换3. 字符级别的转换高级转换

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA

MyBatis-Plus逻辑删除实现过程

《MyBatis-Plus逻辑删除实现过程》本文介绍了MyBatis-Plus如何实现逻辑删除功能,包括自动填充字段、配置与实现步骤、常见应用场景,并展示了如何使用remove方法进行逻辑删除,逻辑删... 目录1. 逻辑删除的必要性编程1.1 逻辑删除的定义1.2 逻辑删php除的优点1.3 适用场景2.

SpringBoot简单整合ElasticSearch实践

《SpringBoot简单整合ElasticSearch实践》Elasticsearch支持结构化和非结构化数据检索,通过索引创建和倒排索引文档,提高搜索效率,它基于Lucene封装,分为索引库、类型... 目录一:ElasticSearch支持对结构化和非结构化的数据进行检索二:ES的核心概念Index:

Python数据验证神器Pydantic库的使用和实践中的避坑指南

《Python数据验证神器Pydantic库的使用和实践中的避坑指南》Pydantic是一个用于数据验证和设置的库,可以显著简化API接口开发,文章通过一个实际案例,展示了Pydantic如何在生产环... 目录1️⃣ 崩溃时刻:当你的API接口又双叒崩了!2️⃣ 神兵天降:3行代码解决验证难题3️⃣ 深度

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp