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 实现音视频实时通信方案的更多相关文章
- JMeter扩展Java请求实现WebRTC本地音视频推流压测脚本
WebRTC是Web Real-Time Communication缩写,指网页即时通讯,是一个支持Web浏览器进行实时语音或视频对话的API,实现了基于网页的视频会议,比如声网的Agora Web ...
- Vue + WebRTC 实现音视频直播(附自定义播放器样式)
1. 什么是WebRTC 1.1 WebRTC简介 WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频 ...
- libstagefright 音视频同步方案
1:音视频数据都有一个list,用于存放解码后的数据: List mFilledBuffers; 2:解码后的音视频数据不断的往list中存放,不做音视频同步方面的时间上控制 mFille ...
- Android IOS WebRTC 音视频开发总结(二十)-- 自由职业
咋看标题感觉与WebRTC和音视频无关,其实有着很大的关联,文章来自博客园RTC.Blacker,转载请说明出处. 背景: 一方面因为对开发人员比较了解,不喜欢约束,喜欢自由自在,所以我们向往自由职业 ...
- WebRTC 音视频开发
WebRTC 音视频开发 webrtc Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译 ...
- 实时音视频互动系列(下):基于 WebRTC 技术的实战解析
在 WebRTC 项目中,又拍云团队做到了覆盖系统全局,保证项目进程流畅.这牵涉到主要三大块技术点: 网络端.服务端的开发和传输算法 WebRTC 协议中牵扯到服务端的应用协议和信令服务 客户端iOS ...
- 腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践
1.概述 本文来自腾讯视频云终端技术总监rexchang(常青)技术分享,内容分别介绍了微信小程序视音视频和WebRTC的技术特征.差异等,并针对两者的技术差异分享和总结了微信小程序视音视频和WebR ...
- 了不起的WebRTC:生态日趋完善,或将实时音视频技术白菜化
本文原文由声网WebRTC技术专家毛玉杰分享. 1.前言 有人说 2017 年是 WebRTC 的转折之年,2018 年将是 WebRTC 的爆发之年,这并非没有根据.就在去年(2017年),WebR ...
- 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)
随笔分类 - webrtc Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...
- iOS - WebRTC 自编译(音视频即时通讯开源库)
什么是WebRTC? WebRTC,名称源自网页实时通信(Web Real-Time Communication)的缩写,简而言之它是一个支持网页浏览器进行实时语音对话或视频对话的技术.是谷歌2010 ...
随机推荐
- openGauss数据库将磁盘表转换为MOT
openGauss 数据库将磁盘表转换为 MOT 一.将磁盘表转换为 MOT 方法 磁盘表直接转换为 MOT 尚不能实现,这意味着尚不存在将基于磁盘的表转换为 MOT 的 ALTER TABLE 语句 ...
- os.path.splitext
os.path.splitext是Python标准库中的一个函数,它可以将一个文件路径拆分成两部分:文件名和文件扩展名.例如: 点击查看代码 import os file_path='avercrop ...
- HDC2021技术分论坛:鸿蒙智联平台——智能硬件伙伴的最佳拍档
作者:chengjie,鸿蒙智联生态服务平台运营经理 您在产品开发和运营过程中是否遇到过以下问题: 产品开发千头万绪,无从下手?产品上市后不能清晰地了解消费者的使用情况?用户的意见无法及时传递和答复, ...
- HarmonyOS Connect FAQ第三期
原文:https://mp.weixin.qq.com/s/YpI9-k4yQvNhaMfg7Li82g,点击链接查看更多技术内容. 在开发HarmonyOS Connect生态产品时,你是否对设 ...
- GAN的实现和一些问题
GAN的学习是一个二人博弈问题,最终目标是达到纳什平衡.对抗指的是生成网络和判别网络的互相对抗.生成网络尽可能生成逼真样本,判别网络则尽可能去判别该样本是真实样本,还是生成的假样本.示意图如下: 生成 ...
- React的理解以及特性
一.是什么 React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案 遵循组件设计模式.声明式编程范式和函数式编程概念,以使前端应用程序更高效 使用虚拟DOM来有效地操作 ...
- 第二課:Mirth培養興趣之旅 ——由定時刷庫接口編程講起
1.准备工作 1.1 本机安装vs2019:(https://visualstudio.microsoft.com/zh-hans/) 1.2 本机安装win64的MariaDB 10.3.27版本数 ...
- 力扣77(Java)-组合(中等)
题目: 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合. 你可以按 任何顺序 返回答案. 示例 1: 输入:n = 4, k = 2输出:[ [2,4], [3,4], ...
- 如何通过 Serverless 提高 Java 微服务治理效率?
简介: 在业务初期,因人手有限,想要快速开发并上线产品,很多团队使用单体的架构来开发.但是随着公司的发展,会不断往系统里面添加新的业务功能,系统越来越庞大,需求不断增加,越来越多的人也会加入到开发团队 ...
- 链路分析 K.O “五大经典问题”
简介:链路分析是基于已存储的全量链路明细数据,自由组合筛选条件与聚合维度进行实时分析,可以满足不同场景的自定义诊断需求. 作者:涯海 链路追踪的 "第三种玩法" 提起链路追踪,大 ...