首页 > 其他分享 >Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案

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

时间:2024-04-16 22:33:25浏览次数:30  
标签:WebSocket log Swoole 音视频 peer console WebRTC const

原文首发链接: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 可以实现点对点的音视频通信。

标签:WebSocket,log,Swoole,音视频,peer,console,WebRTC,const
From: https://www.cnblogs.com/yxhblogs/p/18117322

相关文章

  • Ubuntu18.04安装opensips一次过,实现sip语音视频通话
    安装方式apt命令安装,不建议使用此方式想要在ubuntu18.04(建议使用18.04,不出错)上通过apt命令安装的可以借鉴一下这篇文章,但是这篇文章中博主有错误并未解决,下面是解决方式执行下列命令,使用opensipsdbctl创建数据库的时候会报错opensipsdbctlcreate#错误信息为ERROR:......
  • 抖音视频评论提取工具|评论ID批量采集下载拓客软件
    解放你的音营销!抖音评论提取工具助你轻松拓客!随着音平台的火爆,如何有效地进行营销和拓客成为了许多企业和个人关注的焦点。针对这一需求,我们推出了全新的评论提取工具,让你轻松抓取关键词相关的视频评论,助力你的营销策略!主要功能:关键词搜索视频评论抓取:只需输入任务名称、......
  • 深入剖析webrtc事件机制 sigslot
    一、什么是信号槽在构建大型C++项目过程中,如何在各个类之间高效且安全地传递数据或事件是一项具有挑战性的任务。最直接但并不推荐的方法是使用全局变量。虽然这种方法简单易用,但它会导致命名冲突,难以维护,且全局变量的值容易在不知情的情况下被意外修改。另一种常见的方......
  • srs+webrtc实现浏览器直播(仿b站页面,纯前端)
    关于srs请看我之前的博客,SRS实现网页和手机端简单直播。与之不同的是,浏览器推流需要使用werbrtc,因此只需要按srs官网配置即可,WebRTC|SRS(ossrs.net)。回到正题... 一.页面搭建b站web直播页面是通过video标签元素实现,但是video并不能同时将摄像头、麦克风、屏幕共享等同时......
  • webrtc分支切换到m94 下载报错 FileNotFoundError: [Errno 2] No such file or direct
    FileNotFoundError:[Errno2]Nosuchfileordirectory:'vpython' 此问题翻遍整个网络,没有解决方案,希望能帮忙到需要的人 描述:      正常下载代码后,基于master(默认)编译通过,现需要切到m94分支(参考 Linux/Ubuntu编译WebRTC&libmediasoupclient_linuxg++......
  • Python实战:利用Python进行音视频处理
    1.引言音视频处理是一种广泛应用于娱乐、教育、医疗等领域的技术,它允许我们编辑、转换和分析音视频数据。Python作为一种功能强大的编程语言,提供了丰富的库和框架,使得音视频处理变得更加高效和便捷。本文将介绍Python在音视频处理中的应用实例。2.环境准备在开始编......
  • Swoole 源码分析之 Timer 定时器模块
    原文首发链接:Swoole源码分析之Timer定时器模块大家好,我是码农先森。引言Swoole中的毫秒精度的定时器。底层基于epoll_wait和setitimer实现,数据结构使用最小堆,可支持添加大量定时器。在同步IO进程中使用setitimer和信号实现,如Manager和TaskWorker进程,在异步IO......
  • 探索基于WebRTC的有感录屏技术开发流程
    第一章:技术原理WebRTC(WebReal-TimeCommunication)是一种开放源代码项目,旨在通过浏览器之间的点对点通信实现实时音视频通信。WebRTC利用JavaScriptAPI在浏览器中实现多媒体通信,无需安装插件或第三方软件。在线录屏|一个覆盖广泛主题工具的高效在线平台(amd794.com)https:/......
  • Android 音视频开发 - VideoView
    Android音视频开发-VideoView本篇文章主要介绍下Android中的VideoView.1:VideoView简介VideoView是一个用于播放视频的视图组件,可以方便地在应用程序中播放本地或网络上的视频文件。VideoView可以直接在布局文件中使用,也可以在代码中动态创建。它封装了MediaPlayer和Sur......
  • Android音视频开发 - MediaMetadataRetriever 相关
    Android音视频开发-MediaMetadataRetriever相关MediaMetadataRetriever是android中用于从媒体文件中提取元数据新的类.可以获取音频,视频和图像文件的各种信息,如时长,标题,封面等.1:初始化对象privateMediaMetadataRetrievermediaMetadataRetriever=newMediaMetadat......