私有化部署WebRTC:基于aiortc实现Web浏览器直接预览远程摄像头

私有化部署WebRTC:基于aiortc实现Web浏览器直接预览远程摄像头

私有化部署WebRTC:基于aiortc实现Web浏览器直接预览远程摄像头

引言:为什么需要这个方案?

在现代物联网和安防监控领域,远程查看摄像头视频流是一个常见需求。传统的解决方案通常采用RTSP协议,但这种方案存在一个致命缺陷:现代Web浏览器(如Chrome、Firefox)无法直接播放RTSP流

之前我尝试了以下方案:

  • 第三方P2P穿透方案:仅支持Windows/Linux Qt客户端,不支持Web浏览器
  • EasyRTC等现成方案:无法私有化部署,稳定性欠佳
  • 直接使用libwebrtc:编译复杂,依赖庞大

今天,我介绍一个基于aiortc的简化方案,它能够:

  1. ✅ 在Web浏览器中直接预览远程摄像头
  2. ✅ 完全私有化部署,数据不经过第三方
  3. ✅ 支持P2P直连、STUN穿透和TURN中转多种连接方式
  4. ✅ 跨平台支持(Windows、Linux、Mac)
  5. ✅ 代码简洁,易于理解和定制

一、WebRTC与aiortc:技术基础

什么是WebRTC?

WebRTC(Web实时通信)是一个开源项目,允许Web浏览器和移动应用进行实时音视频通信和数据传输,而无需安装任何插件。它的核心优势在于:

  1. 点对点通信:数据直接在两个客户端之间传输
  2. 低延迟:专为实时通信设计
  3. 浏览器原生支持:现代浏览器都内置WebRTC API
  4. 自动NAT穿透:通过STUN/TURN技术解决网络障碍

为什么选择aiortc?

aiortc是一个基于Python asyncio的WebRTC实现库,相比原生libwebrtc,它有如下优势:

特性aiortclibwebrtc
安装复杂度pip一键安装需要复杂编译
语言友好性Python,易于集成C++,集成复杂
代码简洁性高级API,代码简洁低级API,代码冗长
灵活性易于自定义和扩展修改困难

二、系统架构设计

我们的解决方案包含四个核心组件:

视频流

信令交换

ICE协商

SDP/ICE转发

SDP/ICE转发

远程设备

WebRTC PeerConnection

Web浏览器

信令服务器

STUN/TURN服务器

组件说明:

  1. STUN/TURN服务器(coturn)
    • STUN:帮助设备发现自己的公网IP和端口
    • TURN:当P2P无法建立时,作为数据中转服务器
  2. 信令服务器(Python WebSocket)
    • 交换WebRTC连接所需的SDP(会话描述协议)和ICE候选地址
    • 管理设备注册和发现
  3. Web服务器(Python HTTP)
    • 提供Web页面给用户访问
    • 集成WebRTC JavaScript客户端
  4. 设备端(Python aiortc)
    • 捕获视频流(摄像头、文件、虚拟视频)
    • 通过WebRTC传输视频到Web端

三、详细部署步骤

步骤1:环境准备

在开始之前,请确保你有一台具有公网IP的云服务器(如阿里云ECS)。我们将在这台服务器上部署所有服务。

首先设置环境变量(这些变量将在后续脚本中使用):

exportECS_IP_ADDR=替换为你的云服务器实际IP地址 exportSTUN_SRV=$ECS_IP_ADDRexportSTUN_PORT=3478exportSIGNALING_SRV=$ECS_IP_ADDR#信令服务器,用来交换WebRTC连接需要的信息exportSIGNALING_PORT=9901exportWEB_SRV=$ECS_IP_ADDR# Web服务器exportWEB_PORT=9900

重要提示:将这些变量添加到你的~/.bashrc文件中,以便每次登录时自动加载。

步骤2:部署STUN/TURN服务器

STUN/TURN服务器是WebRTC的"网络向导",帮助两个设备找到彼此并建立连接。

2.1 安装coturn
# 在CentOS/RHEL系统上安装 yum install coturn -y # 配置coturncat> /etc/coturn/turnserver.conf <<EOF listening-ip=0.0.0.0 listening-port=$STUN_PORT relay-ip=$(ifconfig eth0 |grep -w inet |awk'{print $2}') external-ip=$ECS_IP_ADDR log-file=/var/tmp/turn.log min-port=40000 max-port=65535 EOF systemctl restart coturn systemctl status coturn 
2.2 配置防火墙

云服务器需要开放以下端口:

  • 3478:STUN/TURN服务端口
  • 9900:Web服务端口
  • 9901:信令服务器端口
  • 40000-65535:TURN中继端口范围

在阿里云控制台的安全组中,添加入站规则允许这些端口的TCP和UDP流量。

请添加图片描述
2.3 测试STUN/TURN服务

访问Trickle ICE

请添加图片描述
stun:你的服务器IP:3478 turn:你的服务器IP:3478 

使用用户名webrtc,密码webrtc。如果测试成功,你会看到类似以下结果:

  • srflx:STUN穿透成功
  • relay:TURN中转可用

步骤3:部署信令服务器

信令服务器是WebRTC连接的"媒人",负责交换连接信息。它使用WebSocket协议进行实时通信。

3.1 创建信令服务器脚本
cat > signaling_server.py << EOF import asyncio import json import logging from aiohttp import web import aiohttp_cors logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__)classSignalingServer:def__init__(self): self.app = web.Application() self.clients ={ }# 存储连接的用户 self.setup_routes()defsetup_routes(self): cors = aiohttp_cors.setup(self.app, defaults={ "*": aiohttp_cors.ResourceOptions( allow_credentials=True, expose_headers="*", allow_headers="*",)})# 添加WebSocket路由 resource = cors.add(self.app.router.add_resource('/ws')) cors.add(resource.add_route("GET", self.websocket_handler))# 健康检查路由 self.app.router.add_get('/health', self.health_check)# 获取在线设备列表 self.app.router.add_get('/devices', self.get_devices)asyncdefhealth_check(self, request):return web.Response(text='OK')asyncdefget_devices(self, request):"""获取所有在线的设备""" devices =[]for client_id, client_info in self.clients.items():if client_info['type']=='device': devices.append({ 'id': client_id,'type': client_info['type']})return web.json_response({ 'devices': devices})asyncdefwebsocket_handler(self, request): ws = web.WebSocketResponse()await ws.prepare(request) client_id =None client_type =Nonetry:asyncfor msg in ws:if msg.type== web.WSMsgType.TEXT:try: data = json.loads(msg.data) message_type = data.get('type')if message_type =='register':# 客户端注册 client_id = data.get('client_id') client_type = data.get('client_type')# 'web' 或 'device'if client_id in self.clients:try:await self.clients[client_id]['ws'].close()except:pass self.clients[client_id]={ 'ws': ws,'type': client_type,'client_id': client_id } logger.info(f"Client registered: { client_id} ({ client_type})")await ws.send_str(json.dumps({ 'type':'registered','status':'success'}))elif message_type =='offer':# 转发 offer 到对应的设备端 target_device = data.get('target_device') offer_data = data.get('offer')if target_device in self.clients:await self.clients[target_device]['ws'].send_str(json.dumps({ 'type':'offer','offer': offer_data,'from_client': client_id })) logger.info(f"Offer forwarded from { client_id} to { target_device}")else: logger.warning(f"Target device not found: { target_device}")await ws.send_str(json.dumps({ 'type':'error','message':f'Device { target_device} not found'}))elif message_type =='answer':# 转发 answer 到对应的网页端 target_web = data.get('target_web') answer_data = data.get('answer')if target_web in self.clients:await self.clients[target_web]['ws'].send_str(json.dumps({ 'type':'answer','answer': answer_data,'from_client': client_id })) logger.info(f"Answer forwarded from { client_id} to { target_web}")elif message_type =='ice_candidate':# 转发 ICE candidate target = data.get('target') candidate = data.get('candidate')if target in self.clients:await self.clients[target]['ws'].send_str(json.dumps({ 'type':'ice_candidate','candidate': candidate,'from_client': client_id }))elif message_type =='ping':# 心跳检测await ws.send_str(json.dumps({ 'type':'pong'}))except json.JSONDecodeError as e: logger.error(f"JSON解析错误: { e}")elif msg.type== web.WSMsgType.ERROR: logger.error(f'WebSocket error: { ws.exception()}')except Exception as e: logger.error(f"WebSocket error: { e}")finally:# 清理客户端if client_id and client_id in self.clients:del self.clients[client_id] logger.info(f"Client disconnected: { client_id}")return ws defmain(): server = SignalingServer() logger.info("Starting signaling server on http://0.0.0.0:$SIGNALING_PORT") web.run_app(server.app, host='0.0.0.0', port=$SIGNALING_PORT)if __name__ =='__main__': main() EOF 
3.2 安装依赖并运行
# 安装Python环境(如果使用conda) yum install conda -y conda create -n myenv python=3.12source ~/.bashrc conda activate myenv # 安装Python依赖 pip install aiohttp pip install aiohttp_cors # 注意:脚本中的环境变量需要替换为实际值# 运行信令服务器 python signaling_server.py 

步骤4:部署Web服务器

Web服务器提供用户访问的界面,包含WebRTC JavaScript客户端。

4.1 创建Web服务器
cat > web_server.py << EOF from aiohttp import web import aiohttp_cors import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__)classWebServer:def__init__(self): self.app = web.Application() self.setup_routes()defsetup_routes(self): cors = aiohttp_cors.setup(self.app, defaults={ "*": aiohttp_cors.ResourceOptions( allow_credentials=True, expose_headers="*", allow_headers="*",)})# 主页面 self.app.router.add_get('/', self.index_handler) self.app.router.add_get('/index.html', self.index_handler)# 设备端页面 self.app.router.add_get

Read more

7个用于运行LLM的最佳开源WebUI

7个用于运行LLM的最佳开源WebUI

无论是希望将AI大模型集成到业务流程中,还是寻求企业客户服务自动化,亦或者是希望创建一个强大的个人学习工具。可能都需要考虑数据安全、灵活度以及更具有可控性的使用和开发基础。值得考虑的一个方案是:将大模型(LLM)私有化并且创建一个好用的LLM WebUI系统。 下面,我们推荐7个出色的开源LLM WebUI 系统。 01.Open WebUI(Ollama WebUI) https://github.com/open-webui/open-webui Star:45.7K 开发语言:Python、TypeScript\Svelte Open WebUI是一个可扩展、功能丰富且用户友好的WebUI,旨在完全离线操作。它支持包括Ollama和OpenAI在内的各种LLM运行容器或者API。 产品特点: * 直观的界面:受ChatGPT启发的用户友好型聊天 * 响应式设计:在桌面和移动的上实现流畅的性能 * 轻松安装:使用Docker/Kubernetes轻松安装 * 主题定制:个性化与多个主题 * 高亮:增强代码的可读性 * Markdown LaTeX支持:

Java Web 旅游出行指南_ms ()abo系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

Java Web 旅游出行指南_ms ()abo系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

摘要 随着互联网技术的快速发展,旅游行业逐渐从传统的线下模式转向线上智能化服务。旅游出行指南系统作为一种便捷的信息化工具,能够为用户提供个性化的行程规划、景点推荐、酒店预订等服务,极大地提升了旅游体验的效率和舒适度。当前市场上多数旅游平台功能单一,缺乏智能化推荐和实时数据更新能力,难以满足用户日益增长的个性化需求。因此,开发一款基于现代技术的旅游出行指南系统具有重要的现实意义。关键词:旅游出行指南、智能化服务、行程规划、个性化推荐。 本系统采用SpringBoot2作为后端框架,结合Vue3前端技术实现前后端分离架构,提升系统的可维护性和扩展性。数据库选用MySQL8.0,利用MyBatis-Plus简化数据操作,确保高效的数据存取性能。系统主要功能包括用户管理、景点信息查询、行程规划、酒店预订及评价反馈等模块。通过智能算法分析用户偏好,实现个性化推荐,同时支持多条件筛选和实时数据更新。系统设计注重用户体验,提供响应式界面适配多种终端设备。关键词:SpringBoot2、Vue3、MyBatis-Plus、MySQL8.0、个性化推荐。 数据表设计 用户信息数据表 用户信息数

WebStorm对个人免费开放

WebStorm对个人免费开放

前端开发的普惠革命:JetBrains WebStorm 非商业免费政策深度解析 2024 年 10 月 24 日,正值程序员节来临之际,JetBrains 抛出重磅消息:旗下旗舰级前端开发 IDE WebStorm 正式对非商业用途用户全面免费开放。这一举措不仅延续了 RustRover 的免费许可模式,更标志着专业级 Web 开发工具向大众化普及迈出了关键一步,为全球千万前端开发者带来了实质性利好。 一、政策内核:清晰界定的免费边界与权益 1. 非商业用途的精准定义 JetBrains 在 Toolbox 订阅协议中明确划分了免费使用的适用场景,覆盖群体远超传统教育优惠范畴: * 核心免费场景:包括前端技术学习与技能提升、无商业收益的开源项目贡献、技术博客 / 视频教程等内容创作、个人兴趣导向的 Web 开发(如自制工具、创意 demo)。值得注意的是,即使内容创作通过广告产生间接收益,仍属于非商业范畴。 * 商业付费边界:任何直接或间接获取经济收益的开发活动均需付费,

手把手教你完成libwebkit2gtk-4.1-0安装配置(Ubuntu 22.04)

从零搞定 libwebkit2gtk-4.1-0 安装:Ubuntu 22.04 下的实战避坑指南 你有没有遇到过这样的场景?写好了一个基于 GTK 4 的本地 Web 应用,信心满满地在 Ubuntu 22.04 上运行,结果终端弹出一行红色错误: error while loading shared libraries: libwebkit2gtk-4.1.so.0: cannot open shared object file 别急——这不是你的代码出了问题,而是系统里少了关键运行时库: libwebkit2gtk-4.1-0 。 这个库是现代 Linux 桌面开发中“嵌入网页”的核心技术组件。它让你能在原生应用里无缝展示 HTML 内容,比如 Markdown